The CHANGELOG entries ended up in the wrong order after a rebase
performed while merging a recent PR.
Restore the intended organization of the entries so the file reads
correctly again.
Bump PyJWT from 2.13.0 to 2.14.0 to address the following CVEs
reported by Trivy:
* CVE-2026-102268 (CRITICAL)
* CVE-2026-102266 (HIGH) — authentication bypass via empty HMAC
key acceptance.
* CVE-2026-102267 (HIGH) — verification key substitution via
unvalidated JWKS redirects.
* CVE-2026-102271 (HIGH) — authentication bypass via acceptance
of DER public keys as HMAC.
* CVE-2026-102272 (HIGH) — token forgery via improper Unicode
byte-order mark handling.
* CVE-2026-102273 (HIGH) — token forgery via acceptance of
public JWK containers as HMAC.
Since posthog-js 1.356.0, feature flags are reloaded every 5
minutes by default while the tab is visible. Meet sessions are
long-lived visible tabs, so this multiplied the number of `/flags`
requests we send.
Restore the pre-1.356.0 behavior: only (re)load flags on init and
on identity
Add a `purge_inactive_rooms` management command which permanently
deletes the rooms that were not started for
`ROOM_INACTIVITY_DELETION_DAYS` days. Rooms never started are aged from
their creation date.
Important points :
- The setting is unset by default, which disables the purge.
- Rooms holding a recording their users may still access are kept,
- It refuses to run while unregistered rooms are allowed, as the link
of a purged room would turn into a public unregistered room,
Rooms pile up in database with no way to tell the ones still in use from
the abandoned ones. Record on the room the last time LiveKit reported it
as started, in a new `last_started_at` field which stays NULL until the
first time the room is started on a LiveKit node.
The migration stamps existing rooms with the migration time to avoid
deleting preexisting rooms.
Garage has no web console, so inspecting recordings, transcripts and
summaries locally meant writing aws-cli commands by hand.
For each of recordings, transcripts and summaries:
- `make <folder>-list` lists the files from the most recent, and
- `make <folder>-download-latest` downloads the latest one into
data/<folder>.
Only files with the folder's expected extensions are kept, so the Egress
manifests stored next to recordings are skipped.
The media ingresses and their ExternalName services defaulted to the
MinIO service of the development stack, which is now Garage.
Self-hosted relying on these defaults must set serviceMedia*.host and the
upstream-vhost annotations explicitly, see UPGRADE.md.
The development stacks now run Garage instead of MinIO which is
deprecated.
Garage is a bit stricter than MinIO:
- key IDs and secrets must be at least 8 and 16 characters long
- requests must be signed for its region, so every development env now sets
AWS_S3_REGION_NAME=local;
- cross-origin requests are denied unless the bucket CORS rules allow
them, so a one-shot aws-cli container allows the frontend origin to
upload files straight to the bucket.
Switch from the minio client to the boto3 python client, in the metadata
collector agent and the summary service. AWS_S3_REGION_NAME is passed
as-is to boto3, the region is not looked up from the bucket.
In the dev stack, configure the backend to call the summary service
using its v2 API by default.
Also strip the trailing `/` from the task endpoint, which was
triggering a redirect on every call.
The organization now provides default GitHub templates for pull
requests and issues at the org level, so individual repositories no
longer need to duplicate the same Markdown files.
Delete the local templates so this project uses the organization
defaults, reducing the amount of code we maintain and centralizing
the practice across repositories.
Address the following CVEs reported by Trivy on the LiveKit agent
image against `libssl3t64` 3.5.7-1~deb13u2:
* CVE-2026-63073 — CRITICAL (CVSS 9.8)
* CVE-2026-63072
Bump `libssl3t64` to the patched version to pick up both fixes.
Address the following MEDIUM severity CVEs reported against
`djangorestframework` 3.17.1:
* CVE-2026-73228 (CVSS 5.3)
* CVE-2026-73229 (CVSS 4.3)
Bump `djangorestframework` to the patched version to pick up the
fixes.
Bump `libexpat` from 2.8.4-r0 to 2.8.5-r0 to address the following
HIGH severity CVE, reported by Trivy on the frontend image
(alpine 3.24.1):
* CVE-2026-93990 — expat: XML injection via malformed UTF-16
input.
https://avd.aquasec.com/nvd/cve-2026-93990
Most call sites of `update_metadata` already wrap the call in a
try/except that logs the failure at info level.
Remove the warning log inside `update_metadata` itself to avoid
redundant logs, without losing any information.
`EGRESS_ABORTED` and `EGRESS_FAILED` events were previously ignored, leaving
recordings indefinitely in `ACTIVE` state and potentially blocking subsequent
recordings with 409 errors. Add handling and logging for failed and aborted
egresses, discarding failed recordings while preserving the existing behavior
for savable recordings.
Rename `handle_complete` to `handle_savable` to reflect that it handles both
`EGRESS_COMPLETE` and `EGRESS_LIMIT_REACHED`.
Slight refactor to separate LiveKit event handling from recording concerns as
part of a general separation concern to allow for future SFU swapping.
NB:
- FAILED recordings are currently discarded although exploitable media files
may exist
- There is a theoretical hole: if stop observes EGRESS_FAILED before the
egress_ended webhook is processed, the recording is immediately marked as
FAILED. Since only ACTIVE and STOPPED recordings are savable, the webhook
then skips the failure notification and LiveKit error log. In that rare race
condition, participants may therefore not see the failure toast. We accept
this trade-off for now, as this should be very infrequent.
- Another theoretical hole: There is a short race window where the user
clicks stop while the limit-reached status is being processed. Since the user
explicitly requested the stop, we consider skipping the limit notification
acceptable and do not handle this case.
fix(recording): log aborted worker events at info level
When a user starts a recording or a transcription while no track is
published yet, the recording stays in a "starting" state until an
appropriate track is available.
Show an explicit message on start explaining that the recording
will remain in "starting" state until a track of the required type
is published. The expected track type depends on the recording
type (audio-only vs. audio + video).
This situation was generating a lot of support requests, with users
asking why the recording did not actually start.
`VideoResolutionSubscription` applied the saved reception resolution on
`RoomEvent.TrackPublished`. livekit-client does not raise that event for cameras
that were already sending when the local participant joined, so a user who had
chosen Low definition still received High definition from everyone already in
the meeting, and Low definition only from whoever joined after them. Nothing in
the UI showed the discrepancy: the setting kept displaying Low definition.
Apply the preference to the publications we already know about when the effect
runs, and keep listening on `TrackPublished` — which stays the earliest point to
cap a camera that starts after us — plus `TrackSubscribed`, which is the first
event raised for the cameras that were already sending.
That initial pass also covers a change of preference mid-call, which
`VideoTab.updateExistingRemoteVideoQuality` was doing separately. Removed, it is
now the same code path for joining and for changing the setting.
The three entry points overlap on purpose; the `publication.videoQuality` guard
makes the repeats free. It reads as High definition when nothing was ever
requested, so the default case costs no signal round trip either.
Fixes#1606.
`DockerflowMiddleware` serves the endpoints `/__heartbeat__`, `/__lbheartbeat__
that we use for the kubernetes probes. Sitting at the bottom of
MIDDLEWARE, every Kubernetes probe traversed all middlewares which is not
efficient.
The backend and summary probes had the two Dockerflow endpoints the wrong way
round. `/__lbheartbeat__` returns an unconditional 200 as soon as the server is
up and touches no dependency, while `/__heartbeat__` runs the Dockerflow checks
and answers 500 when one of them errors.
Wired as they were, a database error made `/__heartbeat__` fail on every
backend pod at once, restarting them all. Since a restart cannot fix a
database outage, it's better to use these check on the readiness probe
and start routing traffic when the database is reachable.
- Probe liveness on `/__lbheartbeat__` and readiness on `/__heartbeat__`
- Add a startup probe on `/__lbheartbeat__`, polled every 5s with a
`failureThreshold` of 12, leaving the pod a minute to boot
- Drop `initialDelaySeconds` from liveness and readiness, now that the startup
probe holds them off until the server answers
- Set `timeoutSeconds` to 5s on every probe, up from the 1s Kubernetes default
- Set the readiness `failureThreshold` to 3
The `meet.probes.abstract` helper was missing `periodSeconds` block which means
Kubernetes fell back to its 10s default instead of the chart value.
It's now possible to configure the `failureThreshold` and `successThreshold`.
Builds through the Docker API of the Podman service receive none of the
proxy variables in their RUN steps and fail systematically because of
the proxy rejection. Plain HTTP connections are also rejected by the
proxy with a HTTP 405 method error.
- Passes http_proxy, https_proxy and no_proxy from the shell as build args
- Make the Debian mirror of the agents image a build argument and
override it for bureautix to force https usage