+
Conectado como {identity}
+
Sala: {roomName}
+
+ Estado de conexión: {connectionState}
+
+ {connectError ? (
+
+ Error de conexión: {connectError}
+
+ ) : null}
+
+ )
+}
diff --git a/src/lib/livekit.ts b/src/lib/livekit.ts
new file mode 100644
index 0000000..4f55e72
--- /dev/null
+++ b/src/lib/livekit.ts
@@ -0,0 +1,61 @@
+import "server-only"
+
+import { AccessToken } from "livekit-server-sdk"
+
+// Server-side LiveKit integration: minting access tokens for participants
+// to connect to the self-hosted LiveKit SFU (see LIVEKIT_URL/API_KEY/SECRET
+// in .env). Room name convention: the meeting's `uuid` is used directly as
+// the LiveKit room name (stable, unique, matches the original Laravel
+// version's approach — no separate mapping table needed).
+
+const apiKey = process.env.LIVEKIT_API_KEY
+const apiSecret = process.env.LIVEKIT_API_SECRET
+const url = process.env.LIVEKIT_URL
+
+if (!apiKey || !apiSecret || !url) {
+ throw new Error(
+ "LIVEKIT_URL / LIVEKIT_API_KEY / LIVEKIT_API_SECRET environment variables are not set. Add them to .env (see .env.example)."
+ )
+}
+
+export const LIVEKIT_URL = url
+
+interface MintAccessTokenOptions {
+ /** LiveKit room name — use the meeting's `uuid`. */
+ roomName: string
+ /** Stable per-participant identity, e.g. `user-{id}` or `guest-{uuid}`. */
+ identity: string
+ /** Shown to other participants as `Participant.name`. */
+ displayName: string
+ canPublish?: boolean
+ canSubscribe?: boolean
+ /** Host/co-host get room-admin grants; everyone else gets standard publish/subscribe. */
+ isHost?: boolean
+}
+
+/**
+ * Mints a signed LiveKit access token (JWT) authorizing a participant to
+ * join a room. This only requires the token's signing key/secret to match
+ * the LiveKit server's configuration — it does not itself open a network
+ * connection, so minting succeeds even if the actual WebSocket handshake
+ * against the server would fail due to mismatched credentials.
+ */
+export async function mintAccessToken(
+ options: MintAccessTokenOptions
+): Promise