Skip to content

v1.34.0

Latest

Choose a tag to compare

@lebaudantoine lebaudantoine released this 08 Oct 09:11
· 3 commits to main since this release
v1.34.0

Upgrade

Marketing / Brevo integration now uses django-lasuite

The in-house marketing service (core.services.marketing) has been removed and
replaced by the shared implementation from django-lasuite
(lasuite.marketing). This fixes a bug where updating a user's contact on
Brevo overwrote their list memberships, removing lists set by other
La Suite products. Existing lists are now preserved and merged.

Celery worker required. Newsletter signup on login
(SIGNUP_NEW_USER_TO_MARKETING_EMAIL=True) is now dispatched as an
asynchronous Celery task (lasuite.marketing.tasks.create_or_update_contact)
instead of a synchronous call with a 1s timeout. Make sure a Celery worker is
running alongside the backend, otherwise contacts will never be pushed to Brevo.

Configuration changes. The following environment variables / settings are
removed and no longer read:

  • MARKETING_SERVICE_CLASS
  • BREVO_API_KEY
  • BREVO_API_CONTACT_LIST_IDS
  • BREVO_API_CONTACT_ATTRIBUTES (previous default: {"VISIO_USER": True})
  • BREVO_API_TIMEOUT

They are replaced by a single LASUITE_MARKETING setting, configured through:

Variable Default Description
LASUITE_MARKETING_BACKEND lasuite.marketing.backends.dummy.DummyBackend Backend class path
LASUITE_MARKETING_PARAMETERS {} Keyword arguments passed to the backend

⚠️ The default backend is now a dummy (no-op). If you previously used
Brevo, you must explicitly configure it, otherwise signups are silently dropped:

LASUITE_MARKETING_BACKEND=lasuite.marketing.backends.brevo.BrevoBackend
LASUITE_MARKETING_PARAMETERS={"api_key": "<your-brevo-api-key>", "api_contact_list_ids": [1, 2], "api_contact_attributes": {"VISIO_USER": True}}

Migration mapping:

  • BREVO_API_KEY → api_key
  • BREVO_API_CONTACT_LIST_IDS → api_contact_list_ids
  • BREVO_API_CONTACT_ATTRIBUTES → api_contact_attributes (re-add
    {"VISIO_USER": True} if you relied on the old default)
  • BREVO_API_TIMEOUT → no equivalent (the request runs in a background task)

Note: BREVO_API_KEY used to support being read from a secret file; the API key
now lives inside LASUITE_MARKETING_PARAMETERS, so adapt how you inject that
secret (e.g. build the whole variable from your secret store).

Recording encoding settings replaced by a resolution/profile model

The RECORDING_ENCODING_* settings introduced in v1.16.0 exposed raw encoder
values (width, height, framerate, bitrate). They are replaced by two named and configurable sets of
dimensions, a resolution (default: 540p, 720p, 1080p) and a profile
(default: talking_heads, text, mixed, full), which are resolved to the width, height,
fps and video bitrate.

The following environment variables are no longer read. If they are still set in
your deployment they are silently ignored, and your recordings will be encoded with
the new defaults instead of your tuned values.

Removed variable Replaced by
RECORDING_ENCODING_ENABLED Nothing. A default encoding is now always built (see below). Not RECORDING_CUSTOM_ENCODING_ENABLED, which gates a different feature.
RECORDING_ENCODING_WIDTH The width of the entry selected by RECORDING_ENCODING_DEFAULT_RESOLUTION in RECORDING_ENCODING_AVAILABLE_RESOLUTIONS.
RECORDING_ENCODING_HEIGHT The height of that same entry.
RECORDING_ENCODING_FRAMERATE The fps of the profile selected by RECORDING_ENCODING_DEFAULT_PROFILE in RECORDING_ENCODING_AVAILABLE_PROFILES.
RECORDING_ENCODING_VIDEO_BITRATE_KBPS That profile's kbps.

RECORDING_ENCODING_AUDIO_BITRATE_KBPS and RECORDING_ENCODING_KEY_FRAME_INTERVAL_S
keep their names and meaning. The keyframe interval now defaults to 0 (unset,
encoder's choice) instead of 4.0.

If you never set RECORDING_ENCODING_ENABLED=True

The shipped defaults (RECORDING_ENCODING_DEFAULT_PROFILE=full,
RECORDING_ENCODING_DEFAULT_RESOLUTION=720p) match LiveKit's built-in
H264_720P_30 preset: 1280×720, 30 fps, 3000 kbps H.264 MAIN, 128 kbps AAC.
Video output is therefore unchanged.

Audio and keyframing may not be. These values are now sent explicitly as advanced
EncodingOptions rather than relying on LiveKit's preset, so
RECORDING_ENCODING_AUDIO_BITRATE_KBPS and RECORDING_ENCODING_KEY_FRAME_INTERVAL_S
now apply to every recording. They previously applied only when
RECORDING_ENCODING_ENABLED was True. If you set either of them while the
feature was disabled, they had no effect and now do
; check them before upgrading.

If you never set them, no action is required: 128 kbps AAC is what the preset used,
and the keyframe interval now defaults to 0, which leaves the field unset so the
encoder keeps picking it as before. Set RECORDING_ENCODING_KEY_FRAME_INTERVAL_S=4.0
if you want fixed 4-second keyframes (the value the setting defaulted to while it
was gated behind RECORDING_ENCODING_ENABLED).

To keep letting LiveKit pick the encoding instead, set either default to an empty
value:

RECORDING_ENCODING_DEFAULT_RESOLUTION=
RECORDING_ENCODING_DEFAULT_PROFILE=

If you had tuned RECORDING_ENCODING_* values

Translate your old values into a default resolution and a default profile. Declare your own resolution and/or profile. Both maps are read from the
environment as a single-line Python/JSON dict literal (parsed with
ast.literal_eval, so use double-quoted keys and no trailing commas, and do not
add outer quotes in .env-style files):

RECORDING_ENCODING_AVAILABLE_RESOLUTIONS={"540p": {"width": 960, "height": 540}, "720p": {"width": 1280, "height": 720}, "1080p": {"width": 1920, "height": 1080}}
RECORDING_ENCODING_AVAILABLE_PROFILES={"my_old_profile": {"fps": 15, "kbps": {"540p": 350, "720p": 600, "1080p": 1100}}}
RECORDING_ENCODING_DEFAULT_RESOLUTION=720p
RECORDING_ENCODING_DEFAULT_PROFILE=my_old_profile

Both maps are validated at startup and a malformed one raises a ValueError:

  • every entry of RECORDING_ENCODING_AVAILABLE_RESOLUTIONS must declare width and
    height, and every entry of RECORDING_ENCODING_AVAILABLE_PROFILES an fps and a
    kbps map;
  • every profile must define a kbps entry for exactly the keys of
    RECORDING_ENCODING_AVAILABLE_RESOLUTIONS; overriding one of the two maps usually
    means overriding both;
  • RECORDING_ENCODING_DEFAULT_RESOLUTION and RECORDING_ENCODING_DEFAULT_PROFILE,
    when non-empty, must be keys of their respective map.

Breaking: custom worker services must accept encoding_options

Only concerns deployments pointing RECORDING_WORKER_CLASSES at their own worker
class. The shipped VideoCompositeEgressService and AudioCompositeEgressService
are already updated.

The WorkerService protocol's start() takes a third argument, and the mediator
now always passes it as a keyword when the recording carries no per-recording encoding:

# before
def start(self, room_id: str, recording_id: str) -> str: ...

# now
def start(
    self,
    room_id: str,
    recording_id: str,
    encoding_options: Optional[Dict[str, Any]] = None,
) -> str: ...

Optional: per-recording encoding

RECORDING_CUSTOM_ENCODING_ENABLED (default False) toggles whether the
start-recording API accepts an encoding object
({"resolution": "720p", "profile": "talking_heads"}, profile optional. It
falls back to RECORDING_ENCODING_DEFAULT_PROFILE) that overrides the default for
a single recording. It does not enable or disable the
default encoding, which is built from the two RECORDING_ENCODING_DEFAULT_*
settings either way. Leaving it at False preserves the previous behaviour, where
every recording uses the server-side encoding: requests carrying
options.encoding are rejected with a 400 before the recording is created, so
nothing is persisted and no egress is started.

Before enabling it:

  • clients can only pick keys you declared; there is no way to send a raw width or bitrate
  • as of this implementation, the frontend never sends encoding
  • encoding is accepted but ignored for transcript recordings, whose audio-only
    egress has no video encoding to configure.

See docs/features/recording.md
for the full setting reference, the shipped profile table and the tuning caveats.

What's new

Added

  • ✨(helm) import environment variables from Secrets and ConfigMaps
  • 🔒(backend) throttle meeting link generation
  • 🔒️(backend) add a daily cap on room creation
  • 🔧(summary) add setting to control Sentry traces sampling rate
  • ✨(frontend) let signed-out visitors start a meeting
  • ✨(backend) expose allow_unregistered_rooms in the frontend configuration
  • ✅(frontend) add vitest so the frontend can carry unit tests
  • ♿️(frontend) make participant pagination readable and keyboard reachable #1775

Changed

  • ✨(frontend) warn users when the connection falls back to TURN
  • 🔧(backend) configure the technical documentation url

Fixed

  • 🐛(frontend) enforce recording-mode permissions on the checkboxes
  • 🔒️(agents) fix util-linux CVEs reported by Cyberwatch
  • 🔒️(backend) fix HIGH CVEs in Django and urllib3
  • 🔒️(agents) upgrade libpcre2-8-0 to fix CVE-2026-103111
  • 🔒️(frontend) upgrade pcre2 to fix CVE-2026-103111
  • 🐛(summary) disable default S3 checksums for GCS-compatible storage
  • 🔒️(summary) redact meeting content from Sentry events
  • 🐛(frontend) hide tooltips until they have a computed placement
  • 🐛(brevo) use django-lasuite for marketing management
  • ♿️(frontend) expose loading state to assistive technology
  • 🐛(frontend) honour Keep hand raised when picture-in-picture is open

New Contributors

Full Changelog: v1.33.0...v1.34.0