From 79d82d984805be64ab6354dbfbd2a981f95a859a Mon Sep 17 00:00:00 2001 From: Karol Konkol Date: Tue, 23 Jun 2026 14:02:14 +0200 Subject: [PATCH 1/3] Update moq api --- docs/explanation/moq-with-fishjam.mdx | 24 ++++--- .../tutorials/moq/react-native-publishing.mdx | 38 +++++----- .../moq/react-native-subscribing.mdx | 63 ++++++++--------- docs/tutorials/moq/web-publishing.mdx | 42 +++++------ docs/tutorials/moq/web-subscribing.mdx | 69 ++++++++----------- packages/js-server-sdk | 2 +- .../explanation/moq-with-fishjam.mdx | 24 ++++--- .../tutorials/moq/react-native-publishing.mdx | 38 +++++----- .../moq/react-native-subscribing.mdx | 63 ++++++++--------- .../tutorials/moq/web-publishing.mdx | 42 +++++------ .../tutorials/moq/web-subscribing.mdx | 69 ++++++++----------- 11 files changed, 205 insertions(+), 269 deletions(-) diff --git a/docs/explanation/moq-with-fishjam.mdx b/docs/explanation/moq-with-fishjam.mdx index 5a279ea7..c5993285 100644 --- a/docs/explanation/moq-with-fishjam.mdx +++ b/docs/explanation/moq-with-fishjam.mdx @@ -98,42 +98,44 @@ Access to the relay is controlled by **MoQ tokens** — short-lived JWTs that ar | Publisher token | Write access to a specific path | Streamer | | Subscriber token | Read access to a path or namespace | Viewer | -A token is attached to the relay URL as a query parameter (`?jwt=`). The relay validates the token and enforces its scope before allowing any media to flow. +A token is attached to the relay URL as a query parameter (`?jwt=`). The relay validates the token and enforces its scope before allowing any media to flow. Both the Sandbox API and the Server SDK hand you this full **connection URL** (the relay URL with the token already embedded), so the client connects to it directly without assembling anything. Keeping publisher and subscriber tokens separate ensures that a viewer can never accidentally publish to the stream, and a publisher cannot subscribe to paths it does not own. -## Getting Tokens +## Getting Connection URLs -There are two ways to obtain MoQ tokens, depending on where you are in the development lifecycle. +There are two ways to obtain a MoQ connection URL, depending on where you are in the development lifecycle. ### Sandbox API (prototyping) -The **Sandbox API** is a ready-made backend provided by Fishjam for development and prototyping. It issues tokens without requiring you to build your own server, so you can start streaming immediately. +The **Sandbox API** is a ready-made backend provided by Fishjam for development and prototyping. It issues connection URLs without requiring you to build your own server, so you can start streaming immediately. -To get a publisher token, call: +To get a publisher connection URL, call: ``` GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{PUBLISHER-PATH}/publisher ``` -To get a subscriber token, call: +To get a subscriber connection URL, call: ``` GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{SUBSCRIBER-PATH}/subscriber ``` +Each returns a JSON object with a `connection_url` field — the relay URL with the JWT embedded as `?jwt=` — alongside the raw `token`. + The Sandbox API is **not intended for production** — it has no authentication and is only available in the Sandbox environment. See [What is the Sandbox API?](./sandbox-api-concept) for more context. ### Fishjam Server SDK (production) -In production, your backend generates tokens using the **Fishjam Server SDK**. This gives you full control over who can publish and who can subscribe. +In production, your backend generates connection URLs using the **Fishjam Server SDK**. This gives you full control over who can publish and who can subscribe. -The SDK's `createMoqToken` method accepts either a `publishPath` or a `subscribePath`: +The SDK's `createMoqAccess` method accepts either a `publishPath` or a `subscribePath` and returns the MoQ access details — a `connection_url` (the relay URL with the JWT embedded) alongside the raw `token`: -- `publishPath` — issues a publisher token scoped to that path. -- `subscribePath` — issues a subscriber token scoped to that path or namespace prefix. +- `publishPath` — returns a publisher connection URL scoped to that path. +- `subscribePath` — returns a subscriber connection URL scoped to that path or namespace prefix. -Your backend then delivers each token to the appropriate client (publisher or viewer), which uses it to connect to the relay. +Your backend then delivers each connection URL to the appropriate client (publisher or viewer), which uses it to connect to the relay. See the [Web Publishing](../tutorials/moq/web-publishing) and [Web Subscribing](../tutorials/moq/web-subscribing) tutorials (or their [React Native](../tutorials/moq/react-native-publishing) [counterparts](../tutorials/moq/react-native-subscribing)) for working code examples. diff --git a/docs/tutorials/moq/react-native-publishing.mdx b/docs/tutorials/moq/react-native-publishing.mdx index 2c00b181..a1616fc5 100644 --- a/docs/tutorials/moq/react-native-publishing.mdx +++ b/docs/tutorials/moq/react-native-publishing.mdx @@ -47,12 +47,12 @@ MoQ is a protocol with a well-defined negotiation, so a publisher and a subscrib If you don't have a backend server set up, you can prototype publishing using the [Sandbox API](../../explanation/sandbox-api-concept). -### Obtaining a publisher token +### Obtaining a publisher connection URL For more on what the Sandbox API is and its limitations, see [What is the Sandbox API?](../../explanation/sandbox-api-concept). :::info -To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you don't have them already, see [Sandbox API URL and Fishjam ID](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). +To obtain a MoQ connection URL you'll need your Sandbox API URL. If you don't have it already, see [Sandbox API URL](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). ::: @@ -64,17 +64,16 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do // @noErrors import { useSandbox } from "@fishjam-cloud/react-native-client"; - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; // Inside a React component: - const { getSandboxMoqPublisherToken } = useSandbox({ + const { getSandboxMoqPublisherAccess } = useSandbox({ sandboxApiUrl: SANDBOX_API_URL, }); - // Request a publisher token scoped to the publisher path - const publishToken = await getSandboxMoqPublisherToken(PUBLISHER_PATH); + // Request a publisher connection URL scoped to the publisher path + const { connection_url: publishUrl } = await getSandboxMoqPublisherAccess(PUBLISHER_PATH); ``` @@ -85,14 +84,13 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do ```ts // @noErrors - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; const response = await fetch( `${SANDBOX_API_URL}/moq/${PUBLISHER_PATH}/publisher`, ); - const { token: publishToken } = await response.json(); + const { connection_url: publishUrl } = await response.json(); ``` @@ -102,7 +100,7 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do `react-native-moq` is hooks-based. You open a session against the relay, capture the camera and microphone, and hand them to a publisher. -Build the relay URL using the publisher token, open the session, and publish the camera + microphone tracks: +Open the session with the publisher connection URL, capture the camera and microphone, and publish the tracks: ```tsx // @noErrors @@ -116,16 +114,12 @@ import { useSession, } from "react-native-moq"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; -const publishToken = ""; // from the step above - -// Build the relay URL using the publisher token -const relayUrl = `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${publishToken}`; +const publishUrl = ""; // from the step above function PublishScreen() { - // Open the MoQ session against the Fishjam relay - const session = useSession(relayUrl, (s) => s.connect()); + // Open the MoQ session against the Fishjam relay using the publisher connection URL + const session = useSession(publishUrl, (s) => s.connect()); const camera = useCamera({ position: "front" }); const microphone = useMicrophone(); const publisher = usePublisher(session); @@ -174,7 +168,7 @@ The session owns the connection to the relay; the publisher reuses it. Because p The [Quickstart](#quickstart-with-the-sandbox-api) gets you publishing quickly. In production, your backend generates tokens with proper authorization, so you control who can publish. -A **publisher token** grants write access to a specific path. Generate one on your backend and deliver it to the broadcasting client: +A **publisher connection URL** grants write access to a specific path. Generate one on your backend and deliver it to the broadcasting client: @@ -193,8 +187,8 @@ A **publisher token** grants write access to a specific path. Generate one on yo const streamPath = 'stream-alice'; - // Generate a token that allows publishing to 'stream-alice' - const { token: publishToken } = await fishjamClient.createMoqToken({ + // Generate a connection URL that allows publishing to 'stream-alice' + const { connection_url: publishUrl } = await fishjamClient.createMoqAccess({ publishPath: streamPath, }); ``` @@ -213,14 +207,14 @@ A **publisher token** grants write access to a specific path. Generate one on yo stream_path = 'stream-alice' - # Generate a token that allows publishing to 'stream-alice' - publish_token = fishjam_client.create_moq_token(publish_path=stream_path) + # Generate a connection URL that allows publishing to 'stream-alice' + publish_url = fishjam_client.create_moq_access(publish_path=stream_path).connection_url ``` -Deliver this token to the mobile client, then use it to build the relay URL and connect as described in [Connecting and publishing](#connecting-and-publishing). +Deliver this connection URL to the mobile client, then use it to connect as described in [Connecting and publishing](#connecting-and-publishing). ## See also diff --git a/docs/tutorials/moq/react-native-subscribing.mdx b/docs/tutorials/moq/react-native-subscribing.mdx index ee62f1e1..1ff9680e 100644 --- a/docs/tutorials/moq/react-native-subscribing.mdx +++ b/docs/tutorials/moq/react-native-subscribing.mdx @@ -55,12 +55,12 @@ If you don't have a backend server set up, you can prototype subscribing using t MoQ is a protocol with a well-defined negotiation, so a publisher and a subscriber don't need to use the same client library. A stream published from the browser with [`@moq/publish`](./web-publishing) can be watched with `react-native-moq`, and vice versa. ::: -### Obtaining a subscriber token +### Obtaining a subscriber connection URL For more on what the Sandbox API is and its limitations, see [What is the Sandbox API?](../../explanation/sandbox-api-concept). :::info -To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you don't have them already, see [Sandbox API URL and Fishjam ID](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). +To obtain a MoQ connection URL you'll need your Sandbox API URL. If you don't have it already, see [Sandbox API URL](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). ::: @@ -72,17 +72,16 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do // @noErrors import { useSandbox } from "@fishjam-cloud/react-native-client"; - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; // Inside a React component: - const { getSandboxMoqSubscriberToken } = useSandbox({ + const { getSandboxMoqSubscriberAccess } = useSandbox({ sandboxApiUrl: SANDBOX_API_URL, }); - // Request a subscriber token scoped to the subscriber path - const subscribeToken = await getSandboxMoqSubscriberToken(SUBSCRIBER_PATH); + // Request a subscriber connection URL scoped to the subscriber path + const { connection_url: subscribeUrl } = await getSandboxMoqSubscriberAccess(SUBSCRIBER_PATH); ``` @@ -93,14 +92,13 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do ```ts // @noErrors - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; const response = await fetch( `${SANDBOX_API_URL}/moq/${SUBSCRIBER_PATH}/subscriber`, ); - const { token: subscribeToken } = await response.json(); + const { connection_url: subscribeUrl } = await response.json(); ``` @@ -121,16 +119,12 @@ import { } from "react-native-moq"; import type { BroadcastInfo } from "react-native-moq"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; -const subscribeToken = ""; // from the step above - -// Build the relay URL using the subscriber token -const relayUrl = `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${subscribeToken}`; +const subscribeUrl = ""; // from the step above function WatchScreen() { - // Connect to the Fishjam MoQ relay on mount - const session = useSession(relayUrl, (s) => s.connect()); + // Connect to the Fishjam MoQ relay on mount using the subscriber connection URL + const session = useSession(subscribeUrl, (s) => s.connect()); // Discover broadcasts under the subscriber path const broadcasts = useBroadcasts(session, SUBSCRIBER_PATH); @@ -198,7 +192,7 @@ It exposes imperative `enterFullscreen()` / `exitFullscreen()` methods on its re The [Quickstart](#quickstart-with-the-sandbox-api) gets you watching quickly. In production, your backend generates tokens with proper authorization, so you control who can subscribe. -A **subscriber token** grants read access to a specific path. Generate one on your backend and deliver it to the viewing client: +A **subscriber connection URL** grants read access to a specific path. Generate one on your backend and deliver it to the viewing client: @@ -217,8 +211,8 @@ A **subscriber token** grants read access to a specific path. Generate one on yo const streamPath = 'stream-alice'; - // Generate a token that allows subscribing to 'stream-alice' - const { token: subscribeToken } = await fishjamClient.createMoqToken({ + // Generate a connection URL that allows subscribing to 'stream-alice' + const { connection_url: subscribeUrl } = await fishjamClient.createMoqAccess({ subscribePath: streamPath, }); ``` @@ -237,21 +231,21 @@ A **subscriber token** grants read access to a specific path. Generate one on yo stream_path = 'stream-alice' - # Generate a token that allows subscribing to 'stream-alice' - subscribe_token = fishjam_client.create_moq_token(subscribe_path=stream_path) + # Generate a connection URL that allows subscribing to 'stream-alice' + subscribe_url = fishjam_client.create_moq_access(subscribe_path=stream_path).connection_url ``` -Deliver this token to the mobile client, then use it to build the relay URL and connect as described in [Connecting and subscribing](#connecting-and-subscribing). +Deliver this connection URL to the mobile client, then use it to connect as described in [Connecting and subscribing](#connecting-and-subscribing). ### Subscribe to a namespace When multiple publishers join a room, you won't know their exact paths in advance. Instead of consuming a single path, you can **discover** all broadcasts published under a namespace prefix and subscribe to each one as they appear. -To do this, generate a subscriber token scoped to the room namespace instead of a single stream path. +To do this, generate a subscriber connection URL scoped to the room namespace instead of a single stream path. @@ -267,16 +261,16 @@ To do this, generate a subscriber token scoped to the room namespace instead of const roomName = 'my-room'; - const { token: alicePublisherToken } = await fishjamClient.createMoqToken({ + const { connection_url: alicePublisherUrl } = await fishjamClient.createMoqAccess({ publishPath: roomName + "/alice", }); - const { token: bobPublisherToken } = await fishjamClient.createMoqToken({ + const { connection_url: bobPublisherUrl } = await fishjamClient.createMoqAccess({ publishPath: roomName + "/bob", }); - const { token: namespaceToken } = await fishjamClient.createMoqToken({ + const { connection_url: namespaceUrl } = await fishjamClient.createMoqAccess({ subscribePath: roomName, }); ``` @@ -295,21 +289,21 @@ To do this, generate a subscriber token scoped to the room namespace instead of room_name = 'my-room' - alice_publisher_token = fishjam_client.create_moq_token( + alice_publisher_url = fishjam_client.create_moq_access( publish_path=room_name + "/alice", - ) + ).connection_url - bob_publisher_token = fishjam_client.create_moq_token( + bob_publisher_url = fishjam_client.create_moq_access( publish_path=room_name + "/bob", - ) + ).connection_url - namespace_token = fishjam_client.create_moq_token(subscribe_path=room_name) + namespace_url = fishjam_client.create_moq_access(subscribe_path=room_name).connection_url ``` -On the client, pass the namespace token's relay URL to `useSession`, then call `useBroadcasts(session, roomName)` with the room prefix. It returns a reactive `BroadcastInfo[]` that re-renders every time a publisher joins or leaves — no manual diffing of an announce set. Map over it to mount a player per broadcast: +On the client, pass the namespace connection URL to `useSession`, then call `useBroadcasts(session, roomName)` with the room prefix. It returns a reactive `BroadcastInfo[]` that re-renders every time a publisher joins or leaves — no manual diffing of an announce set. Map over it to mount a player per broadcast: ```tsx // @noErrors @@ -321,14 +315,11 @@ import { } from "react-native-moq"; import type { BroadcastInfo } from "react-native-moq"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; const ROOM_NAME = "my-room"; -const namespaceToken = "token"; - -const relayUrl = `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${namespaceToken}`; +const namespaceUrl = ""; function RoomGrid() { - const session = useSession(relayUrl, (s) => s.connect()); + const session = useSession(namespaceUrl, (s) => s.connect()); // Every broadcast published under the 'my-room' prefix, kept live const broadcasts = useBroadcasts(session, ROOM_NAME); diff --git a/docs/tutorials/moq/web-publishing.mdx b/docs/tutorials/moq/web-publishing.mdx index f55c0d3a..fba64604 100644 --- a/docs/tutorials/moq/web-publishing.mdx +++ b/docs/tutorials/moq/web-publishing.mdx @@ -16,7 +16,7 @@ This tutorial explains how to **publish** a live stream from the browser using [ If you're new to MoQ, then we recommend getting familiar with the [MoQ with Fishjam](../../explanation/moq-with-fishjam) explanation. ::: -To start publishing a MoQ stream, you need two things: a _publisher token_ and the relay URL. We show how to quickly prototype with the [Sandbox API](#quickstart-with-the-sandbox-api) and how to get ready for [production](#production-with-server-sdks). +To start publishing a MoQ stream, you need a _publisher connection URL_ — the relay URL with a publisher token embedded as a `?jwt=` query parameter. We show how to quickly prototype with the [Sandbox API](#quickstart-with-the-sandbox-api) and how to get ready for [production](#production-with-server-sdks). :::tip MoQ is a protocol with a well-defined negotiation, so in theory any compliant MoQ client should work. That said, we recommend using the `@moq` client libraries — the reference TypeScript implementation maintained alongside the protocol. For more details, see the [documentation](https://doc.moq.dev/lib/js/). @@ -26,12 +26,12 @@ MoQ is a protocol with a well-defined negotiation, so in theory any compliant Mo If you don't have a backend server set up, you can prototype publishing using the [Sandbox API](../../explanation/sandbox-api-concept). -### Obtaining a publisher token +### Obtaining a publisher connection URL For more on what the Sandbox API is and its limitations, see [What is the Sandbox API?](../../explanation/sandbox-api-concept). :::info -To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you don't have them already, see [Sandbox API URL and Fishjam ID](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). +To obtain a MoQ connection URL you'll need your Sandbox API URL. If you don't have it already, see [Sandbox API URL](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). ::: @@ -43,17 +43,16 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do // @noErrors import { useSandbox } from "@fishjam-cloud/react-client"; - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; // Inside a React component: - const { getSandboxMoqPublisherToken } = useSandbox({ + const { getSandboxMoqPublisherAccess } = useSandbox({ sandboxApiUrl: SANDBOX_API_URL, }); - // Request a publisher token scoped to the publisher path - const publishToken = await getSandboxMoqPublisherToken(PUBLISHER_PATH); + // Request a publisher connection URL scoped to the publisher path + const { connection_url: publishUrl } = await getSandboxMoqPublisherAccess(PUBLISHER_PATH); ``` @@ -64,14 +63,13 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do ```ts // @noErrors - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; const response = await fetch( `${SANDBOX_API_URL}/moq/${PUBLISHER_PATH}/publisher`, ); - const { token: publishToken } = await response.json(); + const { connection_url: publishUrl } = await response.json(); ``` @@ -85,25 +83,19 @@ Install the MoQ packages: npm install @moq/lite @moq/publish ``` -Use the token to connect to the Fishjam MoQ relay and start broadcasting: +Use the connection URL to connect to the Fishjam MoQ relay and start broadcasting: ```ts // @noErrors import * as Moq from "@moq/lite"; import * as Publish from "@moq/publish"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; -const publishToken = ""; +const publishUrl = ""; // ---cut--- -// Build the relay URL using the publisher token -const relayUrl = new URL( - `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${publishToken}`, -); - -// Connect to the Fishjam MoQ relay -const connection = await Moq.Connection.connect(relayUrl); +// Connect to the Fishjam MoQ relay using the publisher connection URL +const connection = await Moq.Connection.connect(new URL(publishUrl)); const camera = new Publish.Source.Camera({ enabled: true }); const microphone = new Publish.Source.Microphone({ enabled: true }); @@ -130,7 +122,7 @@ The stream is now live on the MoQ relay! Viewers can now start watching it — f The [Quickstart](#quickstart-with-the-sandbox-api) gets you publishing quickly. In production, your backend generates tokens with proper authorization, so you control who can publish. -A **publisher token** grants write access to a specific path. Generate one on your backend and deliver it to the broadcasting client: +A **publisher connection URL** grants write access to a specific path. Generate one on your backend and deliver it to the broadcasting client: @@ -149,8 +141,8 @@ A **publisher token** grants write access to a specific path. Generate one on yo const streamPath = 'stream-alice'; - // Generate a token that allows publishing to 'stream-alice' - const { token: publishToken } = await fishjamClient.createMoqToken({ + // Generate a connection URL that allows publishing to 'stream-alice' + const { connection_url: publishUrl } = await fishjamClient.createMoqAccess({ publishPath: streamPath, }); ``` @@ -169,14 +161,14 @@ A **publisher token** grants write access to a specific path. Generate one on yo stream_path = 'stream-alice' - # Generate a token that allows publishing to 'stream-alice' - publish_token = fishjam_client.create_moq_token(publish_path=stream_path) + # Generate a connection URL that allows publishing to 'stream-alice' + publish_url = fishjam_client.create_moq_access(publish_path=stream_path).connection_url ``` -Deliver this token to the broadcasting client, then use it to connect as described in [Connecting and publishing](#connecting-and-publishing). +Deliver this connection URL to the broadcasting client, then use it to connect as described in [Connecting and publishing](#connecting-and-publishing). ## See also diff --git a/docs/tutorials/moq/web-subscribing.mdx b/docs/tutorials/moq/web-subscribing.mdx index 4e69cf83..d23620e2 100644 --- a/docs/tutorials/moq/web-subscribing.mdx +++ b/docs/tutorials/moq/web-subscribing.mdx @@ -16,7 +16,7 @@ This tutorial explains how to **subscribe** to a live stream and render it in th If you're new to MoQ, then we recommend getting familiar with the [MoQ with Fishjam](../../explanation/moq-with-fishjam) explanation. ::: -To receive a MoQ stream you need one thing: a _subscriber token_. We show how to quickly prototype with the [Sandbox API](#quickstart-with-the-sandbox-api) and how to get ready for [production](#production-with-server-sdks). +To receive a MoQ stream you need one thing: a _subscriber connection URL_ — the relay URL with a subscriber token embedded as a `?jwt=` query parameter. We show how to quickly prototype with the [Sandbox API](#quickstart-with-the-sandbox-api) and how to get ready for [production](#production-with-server-sdks). :::tip MoQ is a protocol with a well-defined negotiation, so in theory any compliant MoQ client should work. That said, we recommend using the [`@moq`](https://github.com/moq-dev/moq) client libraries — the reference TypeScript implementation maintained alongside the protocol. For more details, see the [documentation](https://doc.moq.dev/lib/js/). @@ -26,12 +26,12 @@ MoQ is a protocol with a well-defined negotiation, so in theory any compliant Mo If you don't have a backend server set up, you can prototype subscribing using the [Sandbox API](../../explanation/sandbox-api-concept). -### Obtaining a subscriber token +### Obtaining a subscriber connection URL For more on what the Sandbox API is and its limitations, see [What is the Sandbox API?](../../explanation/sandbox-api-concept). :::info -To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you don't have them already, see [Sandbox API URL and Fishjam ID](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). +To obtain a MoQ connection URL you'll need your Sandbox API URL. If you don't have it already, see [Sandbox API URL](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). ::: @@ -43,17 +43,16 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do // @noErrors import { useSandbox } from "@fishjam-cloud/react-client"; - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; // Inside a React component: - const { getSandboxMoqSubscriberToken } = useSandbox({ + const { getSandboxMoqSubscriberAccess } = useSandbox({ sandboxApiUrl: SANDBOX_API_URL, }); - // Request a subscriber token scoped to the subscriber path - const subscribeToken = await getSandboxMoqSubscriberToken(SUBSCRIBER_PATH); + // Request a subscriber connection URL scoped to the subscriber path + const { connection_url: subscribeUrl } = await getSandboxMoqSubscriberAccess(SUBSCRIBER_PATH); ``` @@ -64,14 +63,13 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do ```ts // @noErrors - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; const response = await fetch( `${SANDBOX_API_URL}/moq/${SUBSCRIBER_PATH}/subscriber`, ); - const { token: subscribeToken } = await response.json(); + const { connection_url: subscribeUrl } = await response.json(); ``` @@ -91,25 +89,19 @@ Add a `` element to your page — `MultiBackend` will paint decoded fram ``` -Then use the token to connect and render the stream onto that canvas: +Then use the connection URL to connect and render the stream onto that canvas: ```ts // @noErrors import * as Moq from "@moq/lite"; import * as Watch from "@moq/watch"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; -const subscribeToken = ""; +const subscribeUrl = ""; // ---cut--- -// Build the relay URL using the subscriber token -const relayUrl = new URL( - `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${subscribeToken}`, -); - -// Connect to the Fishjam MoQ relay -const connection = await Moq.Connection.connect(relayUrl); +// Connect to the Fishjam MoQ relay using the subscriber connection URL +const connection = await Moq.Connection.connect(new URL(subscribeUrl)); // Subscribe to the broadcast const broadcast = new Watch.Broadcast({ @@ -146,7 +138,7 @@ import "@moq/watch/element"; import "@moq/watch/ui"; ``` -Then drop `` wrapping `` into your HTML — pass the relay URL (with the JWT token in the query string) and the broadcast path: +Then drop `` wrapping `` into your HTML — pass the connection URL (returned by the SDK or Sandbox API, with the JWT already in the query string) and the broadcast path: ```html @@ -167,7 +159,7 @@ The element observes attributes like `url`, `name`, `paused`, `muted`, `volume`, The [Quickstart](#quickstart-with-the-sandbox-api) gets you watching quickly. In production, your backend generates tokens with proper authorization, so you control who can subscribe. -A **subscriber token** grants read access to a specific path. Generate one on your backend and deliver it to the viewing client: +A **subscriber connection URL** grants read access to a specific path. Generate one on your backend and deliver it to the viewing client: @@ -186,8 +178,8 @@ A **subscriber token** grants read access to a specific path. Generate one on yo const streamPath = 'stream-alice'; - // Generate a token that allows subscribing to 'stream-alice' - const { token: subscribeToken } = await fishjamClient.createMoqToken({ + // Generate a connection URL that allows subscribing to 'stream-alice' + const { connection_url: subscribeUrl } = await fishjamClient.createMoqAccess({ subscribePath: streamPath, }); ``` @@ -206,21 +198,21 @@ A **subscriber token** grants read access to a specific path. Generate one on yo stream_path = 'stream-alice' - # Generate a token that allows subscribing to 'stream-alice' - subscribe_token = fishjam_client.create_moq_token(subscribe_path=stream_path) + # Generate a connection URL that allows subscribing to 'stream-alice' + subscribe_url = fishjam_client.create_moq_access(subscribe_path=stream_path).connection_url ``` -Deliver this token to the viewing client, then use it to connect as described in [Connecting and subscribing](#connecting-and-subscribing). +Deliver this connection URL to the viewing client, then use it to connect as described in [Connecting and subscribing](#connecting-and-subscribing). ### Subscribe to a namespace When multiple publishers join a room, you won't know their exact paths in advance. Instead of consuming a single path, you can **discover** all broadcasts published under a namespace prefix and subscribe to each one as they appear. -To do this, generate a subscriber token scoped to the room namespace instead of a single stream path. +To do this, generate a subscriber connection URL scoped to the room namespace instead of a single stream path. @@ -236,16 +228,16 @@ To do this, generate a subscriber token scoped to the room namespace instead of const roomName = 'my-room'; - const { token: alicePublisherToken } = await fishjamClient.createMoqToken({ + const { connection_url: alicePublisherUrl } = await fishjamClient.createMoqAccess({ publishPath: roomName + "/alice", }); - const { token: bobPublisherToken } = await fishjamClient.createMoqToken({ + const { connection_url: bobPublisherUrl } = await fishjamClient.createMoqAccess({ publishPath: roomName + "/bob", }); - const { token: namespaceToken } = await fishjamClient.createMoqToken({ + const { connection_url: namespaceUrl } = await fishjamClient.createMoqAccess({ subscribePath: roomName, }); ``` @@ -264,15 +256,15 @@ To do this, generate a subscriber token scoped to the room namespace instead of room_name = 'my-room' - alice_publisher_token = fishjam_client.create_moq_token( + alice_publisher_url = fishjam_client.create_moq_access( publish_path=room_name + "/alice", - ) + ).connection_url - bob_publisher_token = fishjam_client.create_moq_token( + bob_publisher_url = fishjam_client.create_moq_access( publish_path=room_name + "/bob", - ) + ).connection_url - namespace_token = fishjam_client.create_moq_token(subscribe_path=room_name) + namespace_url = fishjam_client.create_moq_access(subscribe_path=room_name).connection_url ``` @@ -286,16 +278,13 @@ import * as Moq from "@moq/lite"; import { Effect, Signal } from "@moq/signals"; import * as Watch from "@moq/watch"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; -const namespaceToken = "token"; +const namespaceUrl = ""; // ---cut--- // Reload manages the WebTransport session: it connects, auto-reconnects on drop, // and exposes `announced` as a reactive Set of publishers currently online. const connection = new Moq.Connection.Reload({ - url: new Signal( - new URL(`https://moq.fishjam.work/${FISHJAM_ID}?jwt=${namespaceToken}`), - ), + url: new Signal(new URL(namespaceUrl)), enabled: new Signal(true), }); diff --git a/packages/js-server-sdk b/packages/js-server-sdk index d70af8c6..59b0878e 160000 --- a/packages/js-server-sdk +++ b/packages/js-server-sdk @@ -1 +1 @@ -Subproject commit d70af8c6517be3f35a70de3a0c4ad33de743db55 +Subproject commit 59b0878ea16cd07f971e536558ff469979cfb1b1 diff --git a/versioned_docs/version-0.28.0/explanation/moq-with-fishjam.mdx b/versioned_docs/version-0.28.0/explanation/moq-with-fishjam.mdx index 5a279ea7..c5993285 100644 --- a/versioned_docs/version-0.28.0/explanation/moq-with-fishjam.mdx +++ b/versioned_docs/version-0.28.0/explanation/moq-with-fishjam.mdx @@ -98,42 +98,44 @@ Access to the relay is controlled by **MoQ tokens** — short-lived JWTs that ar | Publisher token | Write access to a specific path | Streamer | | Subscriber token | Read access to a path or namespace | Viewer | -A token is attached to the relay URL as a query parameter (`?jwt=`). The relay validates the token and enforces its scope before allowing any media to flow. +A token is attached to the relay URL as a query parameter (`?jwt=`). The relay validates the token and enforces its scope before allowing any media to flow. Both the Sandbox API and the Server SDK hand you this full **connection URL** (the relay URL with the token already embedded), so the client connects to it directly without assembling anything. Keeping publisher and subscriber tokens separate ensures that a viewer can never accidentally publish to the stream, and a publisher cannot subscribe to paths it does not own. -## Getting Tokens +## Getting Connection URLs -There are two ways to obtain MoQ tokens, depending on where you are in the development lifecycle. +There are two ways to obtain a MoQ connection URL, depending on where you are in the development lifecycle. ### Sandbox API (prototyping) -The **Sandbox API** is a ready-made backend provided by Fishjam for development and prototyping. It issues tokens without requiring you to build your own server, so you can start streaming immediately. +The **Sandbox API** is a ready-made backend provided by Fishjam for development and prototyping. It issues connection URLs without requiring you to build your own server, so you can start streaming immediately. -To get a publisher token, call: +To get a publisher connection URL, call: ``` GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{PUBLISHER-PATH}/publisher ``` -To get a subscriber token, call: +To get a subscriber connection URL, call: ``` GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{SUBSCRIBER-PATH}/subscriber ``` +Each returns a JSON object with a `connection_url` field — the relay URL with the JWT embedded as `?jwt=` — alongside the raw `token`. + The Sandbox API is **not intended for production** — it has no authentication and is only available in the Sandbox environment. See [What is the Sandbox API?](./sandbox-api-concept) for more context. ### Fishjam Server SDK (production) -In production, your backend generates tokens using the **Fishjam Server SDK**. This gives you full control over who can publish and who can subscribe. +In production, your backend generates connection URLs using the **Fishjam Server SDK**. This gives you full control over who can publish and who can subscribe. -The SDK's `createMoqToken` method accepts either a `publishPath` or a `subscribePath`: +The SDK's `createMoqAccess` method accepts either a `publishPath` or a `subscribePath` and returns the MoQ access details — a `connection_url` (the relay URL with the JWT embedded) alongside the raw `token`: -- `publishPath` — issues a publisher token scoped to that path. -- `subscribePath` — issues a subscriber token scoped to that path or namespace prefix. +- `publishPath` — returns a publisher connection URL scoped to that path. +- `subscribePath` — returns a subscriber connection URL scoped to that path or namespace prefix. -Your backend then delivers each token to the appropriate client (publisher or viewer), which uses it to connect to the relay. +Your backend then delivers each connection URL to the appropriate client (publisher or viewer), which uses it to connect to the relay. See the [Web Publishing](../tutorials/moq/web-publishing) and [Web Subscribing](../tutorials/moq/web-subscribing) tutorials (or their [React Native](../tutorials/moq/react-native-publishing) [counterparts](../tutorials/moq/react-native-subscribing)) for working code examples. diff --git a/versioned_docs/version-0.28.0/tutorials/moq/react-native-publishing.mdx b/versioned_docs/version-0.28.0/tutorials/moq/react-native-publishing.mdx index 2c00b181..a1616fc5 100644 --- a/versioned_docs/version-0.28.0/tutorials/moq/react-native-publishing.mdx +++ b/versioned_docs/version-0.28.0/tutorials/moq/react-native-publishing.mdx @@ -47,12 +47,12 @@ MoQ is a protocol with a well-defined negotiation, so a publisher and a subscrib If you don't have a backend server set up, you can prototype publishing using the [Sandbox API](../../explanation/sandbox-api-concept). -### Obtaining a publisher token +### Obtaining a publisher connection URL For more on what the Sandbox API is and its limitations, see [What is the Sandbox API?](../../explanation/sandbox-api-concept). :::info -To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you don't have them already, see [Sandbox API URL and Fishjam ID](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). +To obtain a MoQ connection URL you'll need your Sandbox API URL. If you don't have it already, see [Sandbox API URL](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). ::: @@ -64,17 +64,16 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do // @noErrors import { useSandbox } from "@fishjam-cloud/react-native-client"; - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; // Inside a React component: - const { getSandboxMoqPublisherToken } = useSandbox({ + const { getSandboxMoqPublisherAccess } = useSandbox({ sandboxApiUrl: SANDBOX_API_URL, }); - // Request a publisher token scoped to the publisher path - const publishToken = await getSandboxMoqPublisherToken(PUBLISHER_PATH); + // Request a publisher connection URL scoped to the publisher path + const { connection_url: publishUrl } = await getSandboxMoqPublisherAccess(PUBLISHER_PATH); ``` @@ -85,14 +84,13 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do ```ts // @noErrors - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; const response = await fetch( `${SANDBOX_API_URL}/moq/${PUBLISHER_PATH}/publisher`, ); - const { token: publishToken } = await response.json(); + const { connection_url: publishUrl } = await response.json(); ``` @@ -102,7 +100,7 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do `react-native-moq` is hooks-based. You open a session against the relay, capture the camera and microphone, and hand them to a publisher. -Build the relay URL using the publisher token, open the session, and publish the camera + microphone tracks: +Open the session with the publisher connection URL, capture the camera and microphone, and publish the tracks: ```tsx // @noErrors @@ -116,16 +114,12 @@ import { useSession, } from "react-native-moq"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; -const publishToken = ""; // from the step above - -// Build the relay URL using the publisher token -const relayUrl = `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${publishToken}`; +const publishUrl = ""; // from the step above function PublishScreen() { - // Open the MoQ session against the Fishjam relay - const session = useSession(relayUrl, (s) => s.connect()); + // Open the MoQ session against the Fishjam relay using the publisher connection URL + const session = useSession(publishUrl, (s) => s.connect()); const camera = useCamera({ position: "front" }); const microphone = useMicrophone(); const publisher = usePublisher(session); @@ -174,7 +168,7 @@ The session owns the connection to the relay; the publisher reuses it. Because p The [Quickstart](#quickstart-with-the-sandbox-api) gets you publishing quickly. In production, your backend generates tokens with proper authorization, so you control who can publish. -A **publisher token** grants write access to a specific path. Generate one on your backend and deliver it to the broadcasting client: +A **publisher connection URL** grants write access to a specific path. Generate one on your backend and deliver it to the broadcasting client: @@ -193,8 +187,8 @@ A **publisher token** grants write access to a specific path. Generate one on yo const streamPath = 'stream-alice'; - // Generate a token that allows publishing to 'stream-alice' - const { token: publishToken } = await fishjamClient.createMoqToken({ + // Generate a connection URL that allows publishing to 'stream-alice' + const { connection_url: publishUrl } = await fishjamClient.createMoqAccess({ publishPath: streamPath, }); ``` @@ -213,14 +207,14 @@ A **publisher token** grants write access to a specific path. Generate one on yo stream_path = 'stream-alice' - # Generate a token that allows publishing to 'stream-alice' - publish_token = fishjam_client.create_moq_token(publish_path=stream_path) + # Generate a connection URL that allows publishing to 'stream-alice' + publish_url = fishjam_client.create_moq_access(publish_path=stream_path).connection_url ``` -Deliver this token to the mobile client, then use it to build the relay URL and connect as described in [Connecting and publishing](#connecting-and-publishing). +Deliver this connection URL to the mobile client, then use it to connect as described in [Connecting and publishing](#connecting-and-publishing). ## See also diff --git a/versioned_docs/version-0.28.0/tutorials/moq/react-native-subscribing.mdx b/versioned_docs/version-0.28.0/tutorials/moq/react-native-subscribing.mdx index ee62f1e1..1ff9680e 100644 --- a/versioned_docs/version-0.28.0/tutorials/moq/react-native-subscribing.mdx +++ b/versioned_docs/version-0.28.0/tutorials/moq/react-native-subscribing.mdx @@ -55,12 +55,12 @@ If you don't have a backend server set up, you can prototype subscribing using t MoQ is a protocol with a well-defined negotiation, so a publisher and a subscriber don't need to use the same client library. A stream published from the browser with [`@moq/publish`](./web-publishing) can be watched with `react-native-moq`, and vice versa. ::: -### Obtaining a subscriber token +### Obtaining a subscriber connection URL For more on what the Sandbox API is and its limitations, see [What is the Sandbox API?](../../explanation/sandbox-api-concept). :::info -To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you don't have them already, see [Sandbox API URL and Fishjam ID](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). +To obtain a MoQ connection URL you'll need your Sandbox API URL. If you don't have it already, see [Sandbox API URL](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). ::: @@ -72,17 +72,16 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do // @noErrors import { useSandbox } from "@fishjam-cloud/react-native-client"; - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; // Inside a React component: - const { getSandboxMoqSubscriberToken } = useSandbox({ + const { getSandboxMoqSubscriberAccess } = useSandbox({ sandboxApiUrl: SANDBOX_API_URL, }); - // Request a subscriber token scoped to the subscriber path - const subscribeToken = await getSandboxMoqSubscriberToken(SUBSCRIBER_PATH); + // Request a subscriber connection URL scoped to the subscriber path + const { connection_url: subscribeUrl } = await getSandboxMoqSubscriberAccess(SUBSCRIBER_PATH); ``` @@ -93,14 +92,13 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do ```ts // @noErrors - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; const response = await fetch( `${SANDBOX_API_URL}/moq/${SUBSCRIBER_PATH}/subscriber`, ); - const { token: subscribeToken } = await response.json(); + const { connection_url: subscribeUrl } = await response.json(); ``` @@ -121,16 +119,12 @@ import { } from "react-native-moq"; import type { BroadcastInfo } from "react-native-moq"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; -const subscribeToken = ""; // from the step above - -// Build the relay URL using the subscriber token -const relayUrl = `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${subscribeToken}`; +const subscribeUrl = ""; // from the step above function WatchScreen() { - // Connect to the Fishjam MoQ relay on mount - const session = useSession(relayUrl, (s) => s.connect()); + // Connect to the Fishjam MoQ relay on mount using the subscriber connection URL + const session = useSession(subscribeUrl, (s) => s.connect()); // Discover broadcasts under the subscriber path const broadcasts = useBroadcasts(session, SUBSCRIBER_PATH); @@ -198,7 +192,7 @@ It exposes imperative `enterFullscreen()` / `exitFullscreen()` methods on its re The [Quickstart](#quickstart-with-the-sandbox-api) gets you watching quickly. In production, your backend generates tokens with proper authorization, so you control who can subscribe. -A **subscriber token** grants read access to a specific path. Generate one on your backend and deliver it to the viewing client: +A **subscriber connection URL** grants read access to a specific path. Generate one on your backend and deliver it to the viewing client: @@ -217,8 +211,8 @@ A **subscriber token** grants read access to a specific path. Generate one on yo const streamPath = 'stream-alice'; - // Generate a token that allows subscribing to 'stream-alice' - const { token: subscribeToken } = await fishjamClient.createMoqToken({ + // Generate a connection URL that allows subscribing to 'stream-alice' + const { connection_url: subscribeUrl } = await fishjamClient.createMoqAccess({ subscribePath: streamPath, }); ``` @@ -237,21 +231,21 @@ A **subscriber token** grants read access to a specific path. Generate one on yo stream_path = 'stream-alice' - # Generate a token that allows subscribing to 'stream-alice' - subscribe_token = fishjam_client.create_moq_token(subscribe_path=stream_path) + # Generate a connection URL that allows subscribing to 'stream-alice' + subscribe_url = fishjam_client.create_moq_access(subscribe_path=stream_path).connection_url ``` -Deliver this token to the mobile client, then use it to build the relay URL and connect as described in [Connecting and subscribing](#connecting-and-subscribing). +Deliver this connection URL to the mobile client, then use it to connect as described in [Connecting and subscribing](#connecting-and-subscribing). ### Subscribe to a namespace When multiple publishers join a room, you won't know their exact paths in advance. Instead of consuming a single path, you can **discover** all broadcasts published under a namespace prefix and subscribe to each one as they appear. -To do this, generate a subscriber token scoped to the room namespace instead of a single stream path. +To do this, generate a subscriber connection URL scoped to the room namespace instead of a single stream path. @@ -267,16 +261,16 @@ To do this, generate a subscriber token scoped to the room namespace instead of const roomName = 'my-room'; - const { token: alicePublisherToken } = await fishjamClient.createMoqToken({ + const { connection_url: alicePublisherUrl } = await fishjamClient.createMoqAccess({ publishPath: roomName + "/alice", }); - const { token: bobPublisherToken } = await fishjamClient.createMoqToken({ + const { connection_url: bobPublisherUrl } = await fishjamClient.createMoqAccess({ publishPath: roomName + "/bob", }); - const { token: namespaceToken } = await fishjamClient.createMoqToken({ + const { connection_url: namespaceUrl } = await fishjamClient.createMoqAccess({ subscribePath: roomName, }); ``` @@ -295,21 +289,21 @@ To do this, generate a subscriber token scoped to the room namespace instead of room_name = 'my-room' - alice_publisher_token = fishjam_client.create_moq_token( + alice_publisher_url = fishjam_client.create_moq_access( publish_path=room_name + "/alice", - ) + ).connection_url - bob_publisher_token = fishjam_client.create_moq_token( + bob_publisher_url = fishjam_client.create_moq_access( publish_path=room_name + "/bob", - ) + ).connection_url - namespace_token = fishjam_client.create_moq_token(subscribe_path=room_name) + namespace_url = fishjam_client.create_moq_access(subscribe_path=room_name).connection_url ``` -On the client, pass the namespace token's relay URL to `useSession`, then call `useBroadcasts(session, roomName)` with the room prefix. It returns a reactive `BroadcastInfo[]` that re-renders every time a publisher joins or leaves — no manual diffing of an announce set. Map over it to mount a player per broadcast: +On the client, pass the namespace connection URL to `useSession`, then call `useBroadcasts(session, roomName)` with the room prefix. It returns a reactive `BroadcastInfo[]` that re-renders every time a publisher joins or leaves — no manual diffing of an announce set. Map over it to mount a player per broadcast: ```tsx // @noErrors @@ -321,14 +315,11 @@ import { } from "react-native-moq"; import type { BroadcastInfo } from "react-native-moq"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; const ROOM_NAME = "my-room"; -const namespaceToken = "token"; - -const relayUrl = `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${namespaceToken}`; +const namespaceUrl = ""; function RoomGrid() { - const session = useSession(relayUrl, (s) => s.connect()); + const session = useSession(namespaceUrl, (s) => s.connect()); // Every broadcast published under the 'my-room' prefix, kept live const broadcasts = useBroadcasts(session, ROOM_NAME); diff --git a/versioned_docs/version-0.28.0/tutorials/moq/web-publishing.mdx b/versioned_docs/version-0.28.0/tutorials/moq/web-publishing.mdx index f55c0d3a..fba64604 100644 --- a/versioned_docs/version-0.28.0/tutorials/moq/web-publishing.mdx +++ b/versioned_docs/version-0.28.0/tutorials/moq/web-publishing.mdx @@ -16,7 +16,7 @@ This tutorial explains how to **publish** a live stream from the browser using [ If you're new to MoQ, then we recommend getting familiar with the [MoQ with Fishjam](../../explanation/moq-with-fishjam) explanation. ::: -To start publishing a MoQ stream, you need two things: a _publisher token_ and the relay URL. We show how to quickly prototype with the [Sandbox API](#quickstart-with-the-sandbox-api) and how to get ready for [production](#production-with-server-sdks). +To start publishing a MoQ stream, you need a _publisher connection URL_ — the relay URL with a publisher token embedded as a `?jwt=` query parameter. We show how to quickly prototype with the [Sandbox API](#quickstart-with-the-sandbox-api) and how to get ready for [production](#production-with-server-sdks). :::tip MoQ is a protocol with a well-defined negotiation, so in theory any compliant MoQ client should work. That said, we recommend using the `@moq` client libraries — the reference TypeScript implementation maintained alongside the protocol. For more details, see the [documentation](https://doc.moq.dev/lib/js/). @@ -26,12 +26,12 @@ MoQ is a protocol with a well-defined negotiation, so in theory any compliant Mo If you don't have a backend server set up, you can prototype publishing using the [Sandbox API](../../explanation/sandbox-api-concept). -### Obtaining a publisher token +### Obtaining a publisher connection URL For more on what the Sandbox API is and its limitations, see [What is the Sandbox API?](../../explanation/sandbox-api-concept). :::info -To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you don't have them already, see [Sandbox API URL and Fishjam ID](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). +To obtain a MoQ connection URL you'll need your Sandbox API URL. If you don't have it already, see [Sandbox API URL](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). ::: @@ -43,17 +43,16 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do // @noErrors import { useSandbox } from "@fishjam-cloud/react-client"; - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; // Inside a React component: - const { getSandboxMoqPublisherToken } = useSandbox({ + const { getSandboxMoqPublisherAccess } = useSandbox({ sandboxApiUrl: SANDBOX_API_URL, }); - // Request a publisher token scoped to the publisher path - const publishToken = await getSandboxMoqPublisherToken(PUBLISHER_PATH); + // Request a publisher connection URL scoped to the publisher path + const { connection_url: publishUrl } = await getSandboxMoqPublisherAccess(PUBLISHER_PATH); ``` @@ -64,14 +63,13 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do ```ts // @noErrors - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; const response = await fetch( `${SANDBOX_API_URL}/moq/${PUBLISHER_PATH}/publisher`, ); - const { token: publishToken } = await response.json(); + const { connection_url: publishUrl } = await response.json(); ``` @@ -85,25 +83,19 @@ Install the MoQ packages: npm install @moq/lite @moq/publish ``` -Use the token to connect to the Fishjam MoQ relay and start broadcasting: +Use the connection URL to connect to the Fishjam MoQ relay and start broadcasting: ```ts // @noErrors import * as Moq from "@moq/lite"; import * as Publish from "@moq/publish"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; const PUBLISHER_PATH = "stream-alice"; -const publishToken = ""; +const publishUrl = ""; // ---cut--- -// Build the relay URL using the publisher token -const relayUrl = new URL( - `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${publishToken}`, -); - -// Connect to the Fishjam MoQ relay -const connection = await Moq.Connection.connect(relayUrl); +// Connect to the Fishjam MoQ relay using the publisher connection URL +const connection = await Moq.Connection.connect(new URL(publishUrl)); const camera = new Publish.Source.Camera({ enabled: true }); const microphone = new Publish.Source.Microphone({ enabled: true }); @@ -130,7 +122,7 @@ The stream is now live on the MoQ relay! Viewers can now start watching it — f The [Quickstart](#quickstart-with-the-sandbox-api) gets you publishing quickly. In production, your backend generates tokens with proper authorization, so you control who can publish. -A **publisher token** grants write access to a specific path. Generate one on your backend and deliver it to the broadcasting client: +A **publisher connection URL** grants write access to a specific path. Generate one on your backend and deliver it to the broadcasting client: @@ -149,8 +141,8 @@ A **publisher token** grants write access to a specific path. Generate one on yo const streamPath = 'stream-alice'; - // Generate a token that allows publishing to 'stream-alice' - const { token: publishToken } = await fishjamClient.createMoqToken({ + // Generate a connection URL that allows publishing to 'stream-alice' + const { connection_url: publishUrl } = await fishjamClient.createMoqAccess({ publishPath: streamPath, }); ``` @@ -169,14 +161,14 @@ A **publisher token** grants write access to a specific path. Generate one on yo stream_path = 'stream-alice' - # Generate a token that allows publishing to 'stream-alice' - publish_token = fishjam_client.create_moq_token(publish_path=stream_path) + # Generate a connection URL that allows publishing to 'stream-alice' + publish_url = fishjam_client.create_moq_access(publish_path=stream_path).connection_url ``` -Deliver this token to the broadcasting client, then use it to connect as described in [Connecting and publishing](#connecting-and-publishing). +Deliver this connection URL to the broadcasting client, then use it to connect as described in [Connecting and publishing](#connecting-and-publishing). ## See also diff --git a/versioned_docs/version-0.28.0/tutorials/moq/web-subscribing.mdx b/versioned_docs/version-0.28.0/tutorials/moq/web-subscribing.mdx index 4e69cf83..d23620e2 100644 --- a/versioned_docs/version-0.28.0/tutorials/moq/web-subscribing.mdx +++ b/versioned_docs/version-0.28.0/tutorials/moq/web-subscribing.mdx @@ -16,7 +16,7 @@ This tutorial explains how to **subscribe** to a live stream and render it in th If you're new to MoQ, then we recommend getting familiar with the [MoQ with Fishjam](../../explanation/moq-with-fishjam) explanation. ::: -To receive a MoQ stream you need one thing: a _subscriber token_. We show how to quickly prototype with the [Sandbox API](#quickstart-with-the-sandbox-api) and how to get ready for [production](#production-with-server-sdks). +To receive a MoQ stream you need one thing: a _subscriber connection URL_ — the relay URL with a subscriber token embedded as a `?jwt=` query parameter. We show how to quickly prototype with the [Sandbox API](#quickstart-with-the-sandbox-api) and how to get ready for [production](#production-with-server-sdks). :::tip MoQ is a protocol with a well-defined negotiation, so in theory any compliant MoQ client should work. That said, we recommend using the [`@moq`](https://github.com/moq-dev/moq) client libraries — the reference TypeScript implementation maintained alongside the protocol. For more details, see the [documentation](https://doc.moq.dev/lib/js/). @@ -26,12 +26,12 @@ MoQ is a protocol with a well-defined negotiation, so in theory any compliant Mo If you don't have a backend server set up, you can prototype subscribing using the [Sandbox API](../../explanation/sandbox-api-concept). -### Obtaining a subscriber token +### Obtaining a subscriber connection URL For more on what the Sandbox API is and its limitations, see [What is the Sandbox API?](../../explanation/sandbox-api-concept). :::info -To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you don't have them already, see [Sandbox API URL and Fishjam ID](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). +To obtain a MoQ connection URL you'll need your Sandbox API URL. If you don't have it already, see [Sandbox API URL](../../explanation/sandbox-api-concept#sandbox-api-url-and-fishjam-id). ::: @@ -43,17 +43,16 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do // @noErrors import { useSandbox } from "@fishjam-cloud/react-client"; - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; // Inside a React component: - const { getSandboxMoqSubscriberToken } = useSandbox({ + const { getSandboxMoqSubscriberAccess } = useSandbox({ sandboxApiUrl: SANDBOX_API_URL, }); - // Request a subscriber token scoped to the subscriber path - const subscribeToken = await getSandboxMoqSubscriberToken(SUBSCRIBER_PATH); + // Request a subscriber connection URL scoped to the subscriber path + const { connection_url: subscribeUrl } = await getSandboxMoqSubscriberAccess(SUBSCRIBER_PATH); ``` @@ -64,14 +63,13 @@ To obtain a MoQ token you'll need your Fishjam ID and Sandbox API URL. If you do ```ts // @noErrors - const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; const SANDBOX_API_URL = "YOUR_SANDBOX_API_URL"; const response = await fetch( `${SANDBOX_API_URL}/moq/${SUBSCRIBER_PATH}/subscriber`, ); - const { token: subscribeToken } = await response.json(); + const { connection_url: subscribeUrl } = await response.json(); ``` @@ -91,25 +89,19 @@ Add a `` element to your page — `MultiBackend` will paint decoded fram ``` -Then use the token to connect and render the stream onto that canvas: +Then use the connection URL to connect and render the stream onto that canvas: ```ts // @noErrors import * as Moq from "@moq/lite"; import * as Watch from "@moq/watch"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; const SUBSCRIBER_PATH = "stream-alice"; -const subscribeToken = ""; +const subscribeUrl = ""; // ---cut--- -// Build the relay URL using the subscriber token -const relayUrl = new URL( - `https://relay.fishjam.io/${FISHJAM_ID}?jwt=${subscribeToken}`, -); - -// Connect to the Fishjam MoQ relay -const connection = await Moq.Connection.connect(relayUrl); +// Connect to the Fishjam MoQ relay using the subscriber connection URL +const connection = await Moq.Connection.connect(new URL(subscribeUrl)); // Subscribe to the broadcast const broadcast = new Watch.Broadcast({ @@ -146,7 +138,7 @@ import "@moq/watch/element"; import "@moq/watch/ui"; ``` -Then drop `` wrapping `` into your HTML — pass the relay URL (with the JWT token in the query string) and the broadcast path: +Then drop `` wrapping `` into your HTML — pass the connection URL (returned by the SDK or Sandbox API, with the JWT already in the query string) and the broadcast path: ```html @@ -167,7 +159,7 @@ The element observes attributes like `url`, `name`, `paused`, `muted`, `volume`, The [Quickstart](#quickstart-with-the-sandbox-api) gets you watching quickly. In production, your backend generates tokens with proper authorization, so you control who can subscribe. -A **subscriber token** grants read access to a specific path. Generate one on your backend and deliver it to the viewing client: +A **subscriber connection URL** grants read access to a specific path. Generate one on your backend and deliver it to the viewing client: @@ -186,8 +178,8 @@ A **subscriber token** grants read access to a specific path. Generate one on yo const streamPath = 'stream-alice'; - // Generate a token that allows subscribing to 'stream-alice' - const { token: subscribeToken } = await fishjamClient.createMoqToken({ + // Generate a connection URL that allows subscribing to 'stream-alice' + const { connection_url: subscribeUrl } = await fishjamClient.createMoqAccess({ subscribePath: streamPath, }); ``` @@ -206,21 +198,21 @@ A **subscriber token** grants read access to a specific path. Generate one on yo stream_path = 'stream-alice' - # Generate a token that allows subscribing to 'stream-alice' - subscribe_token = fishjam_client.create_moq_token(subscribe_path=stream_path) + # Generate a connection URL that allows subscribing to 'stream-alice' + subscribe_url = fishjam_client.create_moq_access(subscribe_path=stream_path).connection_url ``` -Deliver this token to the viewing client, then use it to connect as described in [Connecting and subscribing](#connecting-and-subscribing). +Deliver this connection URL to the viewing client, then use it to connect as described in [Connecting and subscribing](#connecting-and-subscribing). ### Subscribe to a namespace When multiple publishers join a room, you won't know their exact paths in advance. Instead of consuming a single path, you can **discover** all broadcasts published under a namespace prefix and subscribe to each one as they appear. -To do this, generate a subscriber token scoped to the room namespace instead of a single stream path. +To do this, generate a subscriber connection URL scoped to the room namespace instead of a single stream path. @@ -236,16 +228,16 @@ To do this, generate a subscriber token scoped to the room namespace instead of const roomName = 'my-room'; - const { token: alicePublisherToken } = await fishjamClient.createMoqToken({ + const { connection_url: alicePublisherUrl } = await fishjamClient.createMoqAccess({ publishPath: roomName + "/alice", }); - const { token: bobPublisherToken } = await fishjamClient.createMoqToken({ + const { connection_url: bobPublisherUrl } = await fishjamClient.createMoqAccess({ publishPath: roomName + "/bob", }); - const { token: namespaceToken } = await fishjamClient.createMoqToken({ + const { connection_url: namespaceUrl } = await fishjamClient.createMoqAccess({ subscribePath: roomName, }); ``` @@ -264,15 +256,15 @@ To do this, generate a subscriber token scoped to the room namespace instead of room_name = 'my-room' - alice_publisher_token = fishjam_client.create_moq_token( + alice_publisher_url = fishjam_client.create_moq_access( publish_path=room_name + "/alice", - ) + ).connection_url - bob_publisher_token = fishjam_client.create_moq_token( + bob_publisher_url = fishjam_client.create_moq_access( publish_path=room_name + "/bob", - ) + ).connection_url - namespace_token = fishjam_client.create_moq_token(subscribe_path=room_name) + namespace_url = fishjam_client.create_moq_access(subscribe_path=room_name).connection_url ``` @@ -286,16 +278,13 @@ import * as Moq from "@moq/lite"; import { Effect, Signal } from "@moq/signals"; import * as Watch from "@moq/watch"; -const FISHJAM_ID = "YOUR_FISHJAM_ID"; -const namespaceToken = "token"; +const namespaceUrl = ""; // ---cut--- // Reload manages the WebTransport session: it connects, auto-reconnects on drop, // and exposes `announced` as a reactive Set of publishers currently online. const connection = new Moq.Connection.Reload({ - url: new Signal( - new URL(`https://moq.fishjam.work/${FISHJAM_ID}?jwt=${namespaceToken}`), - ), + url: new Signal(new URL(namespaceUrl)), enabled: new Signal(true), }); From b6dfff5a95e06a1f0fa7d3b2682a9b79cc23b101 Mon Sep 17 00:00:00 2001 From: Karol Konkol Date: Tue, 23 Jun 2026 15:28:18 +0200 Subject: [PATCH 2/3] Update moq api --- docs/explanation/moq-with-fishjam.mdx | 13 ++++++++++--- .../version-0.28.0/explanation/moq-with-fishjam.mdx | 13 ++++++++++--- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/docs/explanation/moq-with-fishjam.mdx b/docs/explanation/moq-with-fishjam.mdx index c5993285..41b6c3a5 100644 --- a/docs/explanation/moq-with-fishjam.mdx +++ b/docs/explanation/moq-with-fishjam.mdx @@ -98,11 +98,11 @@ Access to the relay is controlled by **MoQ tokens** — short-lived JWTs that ar | Publisher token | Write access to a specific path | Streamer | | Subscriber token | Read access to a path or namespace | Viewer | -A token is attached to the relay URL as a query parameter (`?jwt=`). The relay validates the token and enforces its scope before allowing any media to flow. Both the Sandbox API and the Server SDK hand you this full **connection URL** (the relay URL with the token already embedded), so the client connects to it directly without assembling anything. +A token is attached to the relay URL as a query parameter (`?jwt=`). The relay validates the token and enforces its scope before allowing any media to flow. Both the Sandbox API and the Server SDK hand you the authenticated **connection URL** (the relay URL with the token already embedded), so the client can just take it as is and connect. Keeping publisher and subscriber tokens separate ensures that a viewer can never accidentally publish to the stream, and a publisher cannot subscribe to paths it does not own. -## Getting Connection URLs +## Where to get the Connection URL from? There are two ways to obtain a MoQ connection URL, depending on where you are in the development lifecycle. @@ -122,6 +122,12 @@ To get a subscriber connection URL, call: GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{SUBSCRIBER-PATH}/subscriber ``` +To get a full-access connection URL — one that can both publish to and subscribe on the path — call: + +``` +GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{PATH}/full-access +``` + Each returns a JSON object with a `connection_url` field — the relay URL with the JWT embedded as `?jwt=` — alongside the raw `token`. The Sandbox API is **not intended for production** — it has no authentication and is only available in the Sandbox environment. See [What is the Sandbox API?](./sandbox-api-concept) for more context. @@ -130,10 +136,11 @@ The Sandbox API is **not intended for production** — it has no authentication In production, your backend generates connection URLs using the **Fishjam Server SDK**. This gives you full control over who can publish and who can subscribe. -The SDK's `createMoqAccess` method accepts either a `publishPath` or a `subscribePath` and returns the MoQ access details — a `connection_url` (the relay URL with the JWT embedded) alongside the raw `token`: +The SDK's `createMoqAccess` method accepts a `publishPath`, a `subscribePath`, or both, and returns the MoQ access details — a `connection_url` (the relay URL with the JWT embedded) alongside the raw `token`: - `publishPath` — returns a publisher connection URL scoped to that path. - `subscribePath` — returns a subscriber connection URL scoped to that path or namespace prefix. +- both `publishPath` and `subscribePath` — returns a full-access connection URL that can publish and subscribe on those paths. Your backend then delivers each connection URL to the appropriate client (publisher or viewer), which uses it to connect to the relay. diff --git a/versioned_docs/version-0.28.0/explanation/moq-with-fishjam.mdx b/versioned_docs/version-0.28.0/explanation/moq-with-fishjam.mdx index c5993285..41b6c3a5 100644 --- a/versioned_docs/version-0.28.0/explanation/moq-with-fishjam.mdx +++ b/versioned_docs/version-0.28.0/explanation/moq-with-fishjam.mdx @@ -98,11 +98,11 @@ Access to the relay is controlled by **MoQ tokens** — short-lived JWTs that ar | Publisher token | Write access to a specific path | Streamer | | Subscriber token | Read access to a path or namespace | Viewer | -A token is attached to the relay URL as a query parameter (`?jwt=`). The relay validates the token and enforces its scope before allowing any media to flow. Both the Sandbox API and the Server SDK hand you this full **connection URL** (the relay URL with the token already embedded), so the client connects to it directly without assembling anything. +A token is attached to the relay URL as a query parameter (`?jwt=`). The relay validates the token and enforces its scope before allowing any media to flow. Both the Sandbox API and the Server SDK hand you the authenticated **connection URL** (the relay URL with the token already embedded), so the client can just take it as is and connect. Keeping publisher and subscriber tokens separate ensures that a viewer can never accidentally publish to the stream, and a publisher cannot subscribe to paths it does not own. -## Getting Connection URLs +## Where to get the Connection URL from? There are two ways to obtain a MoQ connection URL, depending on where you are in the development lifecycle. @@ -122,6 +122,12 @@ To get a subscriber connection URL, call: GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{SUBSCRIBER-PATH}/subscriber ``` +To get a full-access connection URL — one that can both publish to and subscribe on the path — call: + +``` +GET https://fishjam.io/api/v1/connect/{FISHJAM_ID}/room-manager/moq/{PATH}/full-access +``` + Each returns a JSON object with a `connection_url` field — the relay URL with the JWT embedded as `?jwt=` — alongside the raw `token`. The Sandbox API is **not intended for production** — it has no authentication and is only available in the Sandbox environment. See [What is the Sandbox API?](./sandbox-api-concept) for more context. @@ -130,10 +136,11 @@ The Sandbox API is **not intended for production** — it has no authentication In production, your backend generates connection URLs using the **Fishjam Server SDK**. This gives you full control over who can publish and who can subscribe. -The SDK's `createMoqAccess` method accepts either a `publishPath` or a `subscribePath` and returns the MoQ access details — a `connection_url` (the relay URL with the JWT embedded) alongside the raw `token`: +The SDK's `createMoqAccess` method accepts a `publishPath`, a `subscribePath`, or both, and returns the MoQ access details — a `connection_url` (the relay URL with the JWT embedded) alongside the raw `token`: - `publishPath` — returns a publisher connection URL scoped to that path. - `subscribePath` — returns a subscriber connection URL scoped to that path or namespace prefix. +- both `publishPath` and `subscribePath` — returns a full-access connection URL that can publish and subscribe on those paths. Your backend then delivers each connection URL to the appropriate client (publisher or viewer), which uses it to connect to the relay. From 16b104e43733fb89958942b0fb59357b323f67cc Mon Sep 17 00:00:00 2001 From: Karol Konkol Date: Tue, 23 Jun 2026 18:32:23 +0200 Subject: [PATCH 3/3] update packages --- packages/js-server-sdk | 2 +- packages/python-server-sdk | 2 +- packages/web-client-sdk | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/js-server-sdk b/packages/js-server-sdk index 59b0878e..0e8fe3e0 160000 --- a/packages/js-server-sdk +++ b/packages/js-server-sdk @@ -1 +1 @@ -Subproject commit 59b0878ea16cd07f971e536558ff469979cfb1b1 +Subproject commit 0e8fe3e0eb13127bade0b761ca254aed03cb2c46 diff --git a/packages/python-server-sdk b/packages/python-server-sdk index ef11da89..fdd55700 160000 --- a/packages/python-server-sdk +++ b/packages/python-server-sdk @@ -1 +1 @@ -Subproject commit ef11da8909a9c33d0156ffb0532f5827a5e23a96 +Subproject commit fdd5570033f84a4e8f49bf2ac7fedc019b5b99df diff --git a/packages/web-client-sdk b/packages/web-client-sdk index 29d7e58d..ceda88fa 160000 --- a/packages/web-client-sdk +++ b/packages/web-client-sdk @@ -1 +1 @@ -Subproject commit 29d7e58d0e8147d691a9a39b74b9b4529ab32c98 +Subproject commit ceda88fac251ac2189bd63cb47161985f1801e67