Refactor CONNECTION_TEST_ROOM_MAX_AGE_SECONDS so it is no longer an
independent setting but a quantity derived from (or added on top of)
the token TTL.
This prevents a misconfiguration where the token would outlive the
delete-room callback. In that case, an attacker holding a valid
token could recreate the room after the callback fired and escape
the intended cleanup.
Currently users have no way to reliably test their connection before
joining a room. To address this, we plan to build a connection-test
page.
The testing requires a dedicated LiveKit token, issued without going
through the room API, which is tied to registered meetings, lobby
rules, and longer-lived access tokens.
Introduce a new viewset for all diagnostics-related features. The
first route issues a token for diagnostics, even for anonymous
users. Each request creates a new dedicated room so users never
share the same LiveKit room during tests. Tokens are short-lived
(default 10 minutes) to limit reuse, and the endpoint is throttled
to prevent abuse.
A Celery worker also schedules a callback that deletes the room
after a certain delay, in every case.
Update the API so that, when a user creates a new meeting without
passing an explicit configuration, the user's persisted preferences
are applied as defaults.
This allows a user to, for example, enable the waiting room by
default on every meeting they create.
Add attributes on the User model to persist per-user preferences for
the default link access level and the default room configuration.
The frontend will let users update these preferences and then reuse
them when generating a link through the webapp.
Persisting them on the backend (rather than in application memory
only) ensures the preferences survive across sessions and devices.
Expose the default access level for rooms in the backend settings
response, so the frontend can initialize the global room preferences
UI with the current default value.
Introduce a new viewset that lets the roomkit start a room even when
no WebRTC participant has joined yet.
This is a first entry point that will be extended over time with
more actions a roomkit needs to be able to trigger.
Known limitations:
* The responsibility around SIP rules is currently split between
the telephony feature and the roomkit one. This may need a
refactor later on to consolidate ownership in a single place.
* The default throttle might be too low for production usage and
will likely need to be revisited.
The API serializer was too restrictive on the `sub` field, expecting
a UUID. This worked in our development and production setups because
our Keycloak is configured to emit UUID subs, but it broke for other
providers.
Per the OIDC spec and the DB model, `sub` can be any string. Align
the serializer with this and accept arbitrary string values.
Fixes#1525.
The is_administrable flag was previously read from the room API
response through the room serializer, giving the frontend static
information about the user's rights.
Refactor the frontend so it derives this flag from the participant
role carried in the participant metadata instead.
Two benefits:
* The flag now updates live along with the participant
attributes/metadata, so role changes are reflected immediately.
* It removes the duplication between the API response and the
metadata, which both used to determine the user's capabilities.
The backend previously passed an abstract is_admin_or_owner boolean
flag in the LiveKit token. That kept the frontend minimalistic and
saved it from having to handle role comparisons.
As we introduce more features that need to distinguish between the
room owner and admins, refactor the token to carry the role
directly. The frontend can then derive the relevant flags from a
richer piece of information.
Add an endpoint that allows updating a user's role while in a
meeting. The goal is to let users promote other connected
participants to admin or moderator, so the burden of administrating
a meeting can be shared.
Introduce a new permission class that verifies the caller making a
request is both authenticated and actually present in the call.
It will be used to gate actions that require the user to be live in
the room, for example:
* allowing someone in from the waiting room
* promoting another participant to a different role
More generally, this covers every action where, for security
reasons, we need to make sure the user is truly present in the call
and that someone is not reusing their cookie as an API key.
The info panel was crashing when opening a room that was not
registered in the database (with ALLOW_UNREGISTERED_ROOMS=true),
because the API response did not include the room slug.
Instead of adding frontend fallbacks, update the unregistered-room
response to include the slug, keeping the API contract consistent
with registered rooms.
Note: this still relies on the unregistered-room response staying
aligned with the registered-room schema. Any future divergence
between the two responses could introduce similar issues.
A refactoring on the backend side is needed.
It closes#1441.
Add a new setting that controls whether an authenticated user can
rename or modify their display name, or if they must use the one
returned by the SSO.
When the setting is disabled, only anonymous users can set a display
name freely; authenticated users always use their SSO display name.
Requested by many self-hosters.
This commit introduces the compatibility with summary v2.
It tecnically doesn't break the compatibility with v1 as
v1 params are still sent. But we advise people using the
transcribe feature in their own deployments to adapt to the
new v2 API, as this compatibility will be removed in a
future major version.
* RecordingStatusChoices now has
EXTERNAL_PROCESS_SUCCESSFUL
& EXTERNAL_PROCESS_FAILED
Values, which are changed by a new webhook that
can be called by the transcribe service.
This webhook is protected by its own bearer token.
* Title for the document is computed in visio,
* Tests are added / updated accordingly
Recording lifecycle previously relied exclusively on the MinIO storage-hook
endpoint to transition from STOPPED to SAVED, which made the system dependent
on MinIO bucket notifications and lifecycle configuration. Introduce support
for LiveKit egress_ended webhook (EGRESS_COMPLETE, EGRESS_LIMIT_REACHED) as
an alternative finalization mechanism for self-hosted deployments that do
not use MinIO / S3 hooks.
Change behavior of existing configuation RECORDING_STORAGE_EVENT_ENABLE.
When the LiveKit mechanism is enabled (RECORDING_STORAGE_EVENT_ENABLE=False),
RecordingEventsService.handle_complete is triggered from
LiveKitEventsService._handle_egress_ended.
Since we have added custom backgrounds, they could be
soft deleted but were never really deleted.
This commits introduces a command to actually delete
this files.
In introduces also a command to purge pending upload
.
With the addition of the ANALYSING state
files could be accessed in the short time they
were in that state.
We now require files to be in ready.
Also adds missing frontend types (no impact).
When analysing a file, the previous commit introduced a row level lock
to make sure we would analyse and promote a single file.
This commit changes the locking mechanism so that it happens with
the upload state which avoids long running db locks and potential
perf issues.
Before this commit, post a file check, the policy could be
reused to change the verified file.
Now, files are uplaoded to a temporary location, then inside
a transaction that prevents concurrent calls, the file is
copied to its final destination and the checks are run on that one.
A new file can still be updated with the policy but it will never be read, etc.
As part of this change, all files in the new tmp directory
on s3 should have an expiration policy.
Use the Python port of Google's phone number library on the backend
instead of the JS one on the client. Saves 140Kb of unnecessary JS
and avoids a complex dynamic import that would have been required to
optimize loading.
The transformation is static: the phone number lives in the Django
settings, so the backend can format it once and pass the result to
the client. This avoids every client loading the 140Kb library and
re-computing the same information.
Moreover, within a single client, the transformation was previously
re-computed several times during a webapp lifecycle.
Ensure the participant requesting a mute action is still present
in the room before processing the request.
This mitigates scenarios where a previously issued token could be
reused after the meeting has ended.
Current token lifetime is intentionally long-lived and will be
refactored in the future to better align with LiveKit session
constraints. In the meantime, add this extra validation step to
reduce the attack surface.
Extend the existing live synchronization mechanism beyond room
configuration to also include lobby access level changes.
This ensures that all owners and admins sharing a room maintain a
consistent and up-to-date view of room state in the frontend,
including configuration and access control updates.
Update room serialization to include room configuration for all
users fetching the API response, not only room owners.
This behavior was inherited from the original upstream project.
At the moment, exposing this configuration does not appear to
introduce meaningful security concerns or provide attackers with
additional capabilities.
The decision will continue to be reviewed from a security
perspective, but sharing the configuration improves frontend
consistency and synchronization.
Simplify source serialization and validation logic while improving
type safety around room configuration handling.
Introduce a dedicated TypeScript type matching the backend
Pydantic model more precisely.
Also harmonize track source casing between frontend and backend to
remove redundant conversion logic and resolve#1282.
Introduce synchronization of room configuration changes across
active participants.
When a room configuration is updated through a PUT operation, the
backend now performs an additional LiveKit API call to notify room
participants through a room metadata update event.
This ensures admins and owners quickly see up-to-date settings in
their administration panel. It also prepares the frontend for
automatic updates of unprivileged participants room’s data without
refetching it from the API.
An event-driven design was chosen instead of storing the full room
configuration in LiveKit metadata. While embedding the state
directly in metadata would provide immediate synchronization, it
would also require initializing and maintaining configuration
state during room creation or webhook handling, increasing the
risk of operational failures and regressions.
Instead, the backend emits lightweight synchronization events and
active clients update their React Query cache, which remains the
single source of truth for room configuration data.
Introduce a new room setting controlling whether all participants,
including non-privileged users, can mute others.
Update API validation accordingly and add the frontend controls
allowing administrators to toggle the option and persist the
configuration through the API.
Enable any participant to mute others when the room configuration
allows it. This is enabled by default for all meetings unless
explicitly disabled by an administrator.
Privileged users retain the ability to mute any participant
regardless of the room configuration.
Introduce a new user assignment mechanism to for more friendly output
than the current (SPEAKER_0, SPEAKER_1, ...). Use the VAD metadata to
compare speech intervals with those returned by WhisperX. User with the
highest overlap score above a defined threshold is assigned to each segment.
This method allows for multi-speaker scenarios for a single account.
Wrap Recording and RecordingAccess creation in a single transaction so a
partial failure does not leave orphan rows, and return 409 instead of 500
when a recording is already in progress for the room.
When the worker fails to start, transition the Recording to
FAILED_TO_START so the unique partial constraint on (room, status) no
longer blocks future recording attempts on the same room.
Room.configuration accepted arbitrary JSON without validation, allowing unsafe
or malformed payloads to be stored and creating a security risk. Define a
Pydantic schema to enforce structure and constraints, and add validation
at the serializer level to reject invalid inputs.
Introduce a configuration flag to enable or disable the
application token exchange (service account) mechanism.
This allows activating alternative authentication backends
without requiring full application token configuration.
Required to support the upcoming add-ons authentication backend.
Add a new authentication backend for add-ons, with a core service
managing session state in cache and exposing three API endpoints.
Microsoft Add-ins recommend launching authentication from the
side panel via a dialog, then returning the JWT to the parent
context using postMessage. However, due to Django and SSO security
constraints (window.opener not preserved), this approach is not
viable.
Implement a three-step authentication flow:
- `/init`: create a session, return a short-lived transit token
and CSRF token. Store session ID in a secure HttpOnly cookie.
- `/poll`: allow the add-on to poll until authentication is
complete, then consume the session and clear the cookie.
- `/exchange`: exchange the transit token for a JWT, which is
later retrieved via the `/poll` endpoint.
The add-on opens an authentication dialog, stores the transit
token in sessionStorage, and performs the exchange after login.
This approach works within iframe constraints and provides a
sufficiently secure v0 despite known limitations.
Allow controlling when the metadata collector agent is started,
enabling users to try the feature and disable it if needed.
Introduce a user-level feature flag to toggle the agent for the
initial release.
Introduce MetadataCollector and MetadataCollectorService classes to
centralize the collection and storage of user connections, VAD events,
and chat messages. This creates a structured foundation for future speaker
assignment logic based on voice activity detection. Add tests for this new
feature.
Avoid mapping all Twirp errors to generic 500 responses.
Explicitly handle the case where a participant is no longer in
the room, as this may indicate suspicious behavior or a client
state issue.
Improve error discrimination to provide more accurate responses.
Introduce toggle-hand and rename endpoints in RoomViewSet,
secured with LiveKit token authentication.
Remove direct permission for clients to update their own metadata
via LiveKit tokens to prevent spoofing (e.g. faking admin status).
Proxy participant metadata updates through the backend to enforce
proper validation and authorization.
Signed-off-by: lebaudantoine <lebaud.antoine131@gmail.com>
Serialization hardening introduced a breaking change between the
frontend and backend. Adjust the Pydantic model to restore
compatibility.
Reinstate support for can_subscribe_metric, which is passed by
default from the frontend.
Sanitize error handling to prevent leaking internal details when
invalid or malicious requests are sent to the API.
Return generic error responses to reduce the risk of information
disclosure during probing attempts.
Update the response message to explicitly state that certain
notifications are ignored on purpose, avoiding confusion
during debugging or log inspection.
With the introduction of background file uploads, a misconfigured
MinIO webhook could trigger the storage hook endpoint for unrelated
files.
While the dev setup now filters events via the MinIO lifecycle
configuration, add a safeguard at the application level.
Enforce a stricter filepath regex when parsing storage hook events
and ignore files outside the recording output directory.
Return a clean 200 response to acknowledge the webhook while
avoiding unnecessary processing.
Combine the regex used in Drive with the one from the recording
feature and centralize them in the enums module.
Although the module name suggests only enums, it also hosts
shared constants used across the codebase.
Add FILE_UPLOAD_ENABLED setting (default to False, to avoid
a breaking change). Also adds a max_count_by_user sub setting
to restrict the number of uploaded files per user.
Remove the user-provided filename while preserving the extension,
and switch to the format `uuid.extension`.
This is more natural than the previous `uuid/.extension` format
and avoids confusion.
Update related tests accordingly.
Throttle the request-entry endpoint for authenticated users also
to guard against accidental hammering from buggy clients.
Authenticated users are throttled via RequestEntryUserRateThrottle.
Anonymous users are throttled using the lobby participant cookie
through RequestEntryAnonRateThrottle.
use the lobby participant cookie ID as the throttle cache key
rather than the client IP address.
This avoids penalising multiple users behind the same NAT or proxy
and aligns throttling with how LobbyService identifies participants.
This bug was spotted in production where users from ministries behind
NAT was blocked by throttling while using visio.
If no cookie is present yet, skip throttling for the request. The
cookie will be set on the first response and throttling will apply
from subsequent requests.
This throttle is intended to protect against accidental hammering
from buggy clients, not as a security control against DoS attacks.
This is not a security measure, we should use a WAF.