Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
282 changes: 223 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,105 +1,269 @@
# LiveKit Token Management Service
# 🎥 MatrixRTC Authorization Service

This service is used for two reasons:
- generate JWT tokens with a given LiveKit identity for a given LiveKit room, so that users can use them to authenticate against LiveKit SFU,
- In case of local Matrix users which belong to the same deployment, the corresponding LiveKit room on the SFU will be created if necessary.
The **MatrixRTC Authorization Service** bridges Matrix and LiveKit, handling
authentication and room creation when needed.

It works by allowing a token obtained via the Matrix Client-Server API [OpenID endpoint](https://spec.matrix.org/v1.13/client-server-api/#openid) to be exchanged for a LiveKit JWT token which can be used to access a LiveKit SFU.
## 💡 TL;DR

This functionality is defined by [MSC4195: MatrixRTC using LiveKit backend](https://github.com/matrix-org/matrix-spec-proposals/pull/4195).
Matrix user wants to start or join a call?

Only for Matrix users of homeservers belonging to the same deployment (called local users) corresponding rooms on the LiveKit SFU will be automatically created. Hence, local homeservers need to be declared via the `LIVEKIT_FULL_ACCESS_HOMESERVERS` environment variable (see below).
👤 ➡️ Gets OpenID token ➡️ Sends it to the **MatrixRTC Authorization Service** ➡️
Receives LiveKit JWT ➡️

Note access to LiveKit SFU is restricted for remote users (not belonging to the same deployment). Those users can join existing LiveKit SFU rooms, but missing rooms will not be automatically created to prevent misuse of infrastructure. Due to the SFU selection algorithm and the order of events this will NOT limit or prevent video conferences across Matrix federation.
- **If full-access user** ➡️ Can trigger LiveKit room creation (if needed) ➡️
Joins the call 🎉
- **If restricted user** ➡️ Can join existing rooms ➡️ Joins the call 🎉

## Usage
📡 Once connected, the LiveKit SFU handles all real-time media routing so
participants can see and hear each other.

This service is used when hosting the [Element Call](https://github.com/element-hq/element-call) video conferencing application against a LiveKit backend.
## 🏗️ MatrixRTC Stack: Architecture Overview

Alongside this service, you will need the [LiveKit SFU](https://github.com/livekit/livekit) and for single page applications (SPA) the [Element Call](https://github.com/element-hq/element-call) web application.
<p align="center">
<img src="https://github.com/element-hq/element-call/raw/livekit/docs/Federated_Setup.drawio.png" alt="MatrixRTC Architecture">
</p>

## Installation
## 📌 When to Use

The available releases can be found [here](https://github.com/element-hq/lk-jwt-service/releases).
This service is part of the **MatrixRTC stack** and is primarily used when the
[LiveKit RTC backend (MSC4195)](https://github.com/matrix-org/matrix-spec-proposals/pull/4195)
is in use.

### From docker image
As outlined in the
[Element Call Self-Hosting Guide](https://github.com/element-hq/element-call/blob/livekit/docs/self-hosting.md),
you’ll also need:

- A [LiveKit SFU](https://github.com/livekit/livekit)
- MatrixRTC-compatible clients such as
[Element Call](https://github.com/element-hq/element-call), which can run
either:
- As a standalone Single Page Application (SPA) or
- Embedded for in-app calling

## ✨ What It Does

🔑 **Generates JWT tokens** for a given LiveKit identity and room derived from
the Matrix user and Matrix room, allowing users to authenticate with the LiveKit
SFU.

🛡️ **Manages user access levels** to ensure the proper and secure use of
infrastructure:

- **Full-access users** — Matrix users from homeservers in the same or related
deployment as the MatrixRTC backend. Can trigger automatic LiveKit room
creation if needed.
- **Restricted users** — All other Matrix users. Can join existing LiveKit SFU
rooms, but cannot auto-create new ones.

🏗️ **Auto-creates LiveKit rooms** for full-access users if they don’t already
exist.

> [!NOTE]
> This setup ensures resources are used appropriately while still supporting
> seamless cross-federation MatrixRTC sessions, e.g., video calls. Remote users
> (not on the same deployment) can join existing rooms, but only full-access
> (local) users can trigger room creation. The SFU selection algorithm and event
> ordering ensure that conferences across Matrix federation remain fully
> functional.

## 🗺️ How It Works — Token Exchange Flow

```mermaid
sequenceDiagram
participant U as 🧑 User
participant M as 🏢 Matrix Homeserver
participant A as 🔐 MatrixRTC Authorization Service
participant L as 📡 LiveKit SFU

U->>M: Requests OpenID token
M-->>U: Returns OpenID token
U->>A: Sends OpenID token & room request
A->>M: Validates token via OpenID API
M-->>A: Confirms user identity
A->>A: Generates LiveKit JWT
A->>L: (If full-access user) Create room if missing
A-->>U: Returns LiveKit JWT
U->>L: Connects to room using JWT
```

## 🚀 Installation

Releases are available
**[here](https://github.com/element-hq/lk-jwt-service/releases)**.

### 🐳 From Docker Image

```shell
docker run -e LIVEKIT_URL="ws://somewhere" -e LIVEKIT_KEY=devkey -e LIVEKIT_SECRET=secret -p 8080:8080 ghcr.io/element-hq/lk-jwt-service:0.1.2
docker run -e LIVEKIT_URL="ws://somewhere" -e LIVEKIT_KEY=devkey -e LIVEKIT_SECRET=secret -e LIVEKIT_FULL_ACCESS_HOMESERVERS=example.com -p 8080:8080 ghcr.io/element-hq/lk-jwt-service:0.3.0
```

### From release file
### 📦 From Release File

1. Download the tar file from the URL on the release page:
1. Download & extract:

```shell
wget https://github.com/element-hq/lk-jwt-service/archive/refs/tags/v0.1.1.tar.gz
tar -xvf v0.1.1.tar.gz
mv lk-jwt-service-0.1.1 lk-jwt-service
wget https://github.com/element-hq/lk-jwt-service/archive/refs/tags/v0.3.0.tar.gz
tar -xvf v0.3.0.tar.gz
mv lk-jwt-service-0.3.0 lk-jwt-service
```

2. Build the service:
2. Build:

```shell
cd lk-jwt-service
go build -o lk-jwt-service .
```

3. To start the service locally:
3. Run locally:

```shell
LIVEKIT_URL="ws://somewhere" LIVEKIT_KEY=devkey LIVEKIT_SECRET=secret ./lk-jwt-service
LIVEKIT_URL="ws://somewhere" LIVEKIT_KEY=devkey LIVEKIT_SECRET=secret LIVEKIT_LOCAL_HOMESERVERS=example.com ./lk-jwt-service
```

### Configuration
## ⚙️ Configuration

The service is configured via environment variables:
Set environment variables to configure the service:

Variable | Description | Required
--- | --- | ---
`LIVEKIT_URL` | The websocket URL of the LiveKit SFU | Yes
`LIVEKIT_KEY` or `LIVEKIT_KEY_FROM_FILE` | The API key or key file path for the LiveKit SFU | Yes
`LIVEKIT_SECRET` or `LIVEKIT_SECRET_FROM_FILE` | The secret or secret file path for the LiveKit SFU | Yes
`LIVEKIT_KEY_FILE` | file path to LiveKit SFU key-file format (`APIkey: secret`) | mutually exclusive with `LIVEKIT_KEY` and `LIVEKIT_SECRET`
`LIVEKIT_JWT_PORT` | The port the service listens on | No - defaults to 8080
`LIVEKIT_FULL_ACCESS_HOMESERVERS` | Comma-separated list of Matrix homeservers whose users are authorized with full access to LiveKit SFU features (supports `*` as a wildcard to allow all homeservers) | No - defaults to `*`
| Variable | Description | Required |
| --------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------ |
| `LIVEKIT_URL` | WebSocket URL of the LiveKit SFU | ✅ Yes |
| `LIVEKIT_KEY` / `LIVEKIT_KEY_FROM_FILE` | API key or file path for LiveKit SFU | ✅ Yes |
| `LIVEKIT_SECRET` / `LIVEKIT_SECRET_FROM_FILE` | API secret or file path for LiveKit SFU | ✅ Yes |
| `LIVEKIT_KEY_FILE` | File path with `APIkey: secret` format | ⚠️ mutually exclusive with `LIVEKIT_KEY` and `LIVEKIT_SECRET` |
| `LIVEKIT_JWT_PORT` | Port to listen on (default: `8080`) | ❌ No |
| `LIVEKIT_FULL_ACCESS_HOMESERVERS` | Comma-separated list of fully authorized homeservers (`*` for all) | ❌ Default: `*` |

Please double check that LiveKit SFU room default settings ([config.yaml](https://github.com/livekit/livekit/blob/7350e9933107ecdea4ada8f8bcb0d6ca78b3f8f7/config-sample.yaml#L170)) are configured as
```
> [!IMPORTANT]
> By default, the LiveKit SFU auto-creates rooms for all users. To ensure proper
> access control, update your LiveKit
> [config.yaml](https://github.com/livekit/livekit/blob/7350e9933107ecdea4ada8f8bcb0d6ca78b3f8f7/config-sample.yaml#L170)
> to **disable automatic room creation**.

**LiveKit SFU config should include:**

```yaml
room:
auto_create: false
```

### Reverse Proxy and well-known requirements
## 🔒 Transport Layer Security (TLS) Setup Using a Reverse Proxy

A sample Caddy reverse proxy and well-known configuration (the MAS authenticaion is not required for lk-jwt-service but included for information.):
To properly secure the MatrixRTC Authorization Service, a reverse proxy is
recommended.

```
livekit-jwt.domain.tld {
bind xx.xx.xx.xx
reverse_proxy localhost:8080
### Example Caddy Config

```caddy
matrix-rtc.domain.tld {
bind xx.xx.xx.xx

handle /livekit/jwt* {
reverse_proxy localhost:8080
}
}
```

### Example Nginx Config

```nginx
server {
listen 80;
server_name matrix-rtc.domain.tld;

# Redirect HTTP → HTTPS
return 301 https://$host$request_uri;
}

server {
listen 443 ssl;
server_name matrix-rtc.domain.tld;

# TLS certificate paths (replace with your own)
ssl_certificate /etc/ssl/certs/matrix-rtc.crt;
ssl_certificate_key /etc/ssl/private/matrix-rtc.key;

# TLS settings (minimal)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;

location /livekit/jwt/ {
proxy_pass http://localhost:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
```
handle /.well-known/matrix/* {
header Content-Type application/json
header Access-Control-Allow-Origin * # Only needed if accessed via browser JS

respond /client `{
"m.homeserver": {"base_url": "https://matrix-domain.tld"},
"org.matrix.msc4143.rtc_foci": [{
"type": "livekit",
"livekit_service_url": "https://livekit-jwt.domain.tld"
}],
"org.matrix.msc2965.authentication": {
"issuer": "https://auth.domain.tld/",
"account": "https://auth.domain.tld/account"
}
}`

## 📌 Do Not Forget to Update Your Matrix Site's `.well-known/matrix/client`

For proper MatrixRTC functionality, you need to configure your site's
`.well-known/matrix/client`. See the
[Element Call self-hosting guide](https://github.com/element-hq/element-call/blob/livekit/docs/self-hosting.md#matrixrtc-backend-announcement)
for reference.

The following key must be included in
`https://domain.tld/.well-known/matrix/client`:

```json
"org.matrix.msc4143.rtc_foci": [
{
"type": "livekit",
"livekit_service_url": "https://matrix-rtc.domain.tld/livekit/jwt"
}
]
```
The service is configured via environment variables:

## 🧪 Development & Testing

## Disable TLS verification
### Disable TLS verification

For testing and debugging (e.g. in the absence of trusted certificates while testing in a lab) you can disable TLS verification for the outgoing connection to the Matrix homeserver by setting the environment variable `LIVEKIT_INSECURE_SKIP_VERIFY_TLS` to `YES_I_KNOW_WHAT_I_AM_DOING`.
For testing and debugging (e.g. in the absence of trusted certificates while
testing in a lab), you can disable TLS verification for the outgoing connection
to the Matrix homeserver by setting the environment variable
`LIVEKIT_INSECURE_SKIP_VERIFY_TLS` to `YES_I_KNOW_WHAT_I_AM_DOING`.

### 🛠️ Development Environment (Docker Compose)

Based on the
[Element Call GitHub repo](https://github.com/element-hq/element-call)

The easiest way to spin up the full Matrix stack is by using the development
environment provided by Element Call. For detailed instructions, see
[Element Call Backend Setup](https://github.com/element-hq/element-call?tab=readme-ov-file#backend).

> [!NOTE]
> To ensure your local frontend works properly, you need to add certificate
> exceptions in your browser for:
>
> - `https://localhost:3000`
> - `https://matrix-rtc.m.localhost/livekit/jwt/healthz`
> - `https://synapse.m.localhost/.well-known/matrix/client`
>
> You can do this either by adding the minimal m.localhost CA
> ([dev_tls_m.localhost.crt](https://raw.githubusercontent.com/element-hq/element-call/refs/heads/livekit/backend/dev_tls_m.localhost.crt))
> to your browser’s trusted certificates, or by visiting each URL in your
> browser and following the prompts to accept the exception.

#### 🐳 Start MatrixRTC stack without the MatrixRTC Authorization Service

```sh
git clone https://github.com/element-hq/element-call.git
cd element-call
docker-compose -f ./dev-backend-docker-compose.yml -f ./playwright-backend-docker-compose.override.yml up nginx livekit synapse redis
```

#### 🔑 Start the MatrixRTC Authorization Service locally

```sh
git clone https://github.com/element-hq/lk-jwt-service
cd lk-jwt-service
LIVEKIT_INSECURE_SKIP_VERIFY_TLS="YES_I_KNOW_WHAT_I_AM_DOING" \
LIVEKIT_URL="wss://matrix-rtc.m.localhost/livekit/sfu" \
LIVEKIT_KEY=devkey \
LIVEKIT_SECRET=secret \
LIVEKIT_JWT_PORT=6080 \
LIVEKIT_LOCAL_HOMESERVERS=synapse.m.localhost \
./lk-jwt-service
```
Loading