If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
Changed
Fixed
IMAP:
APPEND and COPY/MOVE into another account's mailbox do not change the JMAP EmailDelivery state.
SEARCH RETURN with MIN or MAX alongside ALL returns only the minimum and maximum in ALL, and SAVE combined with MIN or MAX and ALL or COUNT saves only the minimum and maximum instead of every match.
iTIP: Merging a received update does not copy the VTIMEZONE definitions it carries, so an occurrence moved by an external organizer can reference a TZID (for example W. Europe Standard Time) the stored event no longer defines, and Apple Calendar shows both the original and the moved occurrence.
OAuth: Loopback redirect URIs without a path (for example http://127.0.0.1:45678) are not matched against registered loopback URIs on any port and are rejected by dynamic client registration.
WebDAV: PROPFINDallprop returns DAV:current-user-principal, so Windows Explorer shows every file and folder under the account name.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
sd_notify support.
JMAP: Mailbox/set allows moving a mailbox under a parent that already has a child with the same name, and rejects renames that only change the case of a mailbox name with alreadyExists.
RocksDB: Blob garbage collection rewrites every stored blob on each compaction of the blobs column family, about once per 128MB of new mail.
Milter: End-of-headers is not sent to milters that negotiate SMFIP_NOHDRS without SMFIP_NOEOH, so the transaction stalls until it times out.
OIDC: When the access token lacks the claimUsername claim but has an email claim, the account is built from email without consulting the userinfo endpoint.
JMAP:
onSuccessUpdateEmail and onSuccessDestroyEmail entries in EmailSubmission/set that name an existing submission by id update or destroy the Email with the same id instead of the submission's own Email.
A VacationResponse/set that only sets isEnabled on an account without a vacation response activates an empty Sieve script.
fix dropping UDP packets during connection close (#6278) UDP packets were dropped in case of errors regardless of their size. Now they are dropped only if they are empty and there's an error.
docs: fix youtube rtmps URL (#6286) The rtmps URL for Youtube was wrong. The fingerprint is not required anymore.
prevent free access to playback server in case of legacy credentials (#6311) When legacy credentials are in use (readUser, readPass, publishUser, publishPass), access to the playback server is granted to anyone. Prevent this by granting access only to users using readUser, readPass.
auth: skip JWKS fetch when no JWT is provided (#6274) authenticateJWT fetched the JWKS before checking whether the request carried a token at all. Every anonymous request, such as the first unauthenticatedRTSP DESCRIBE or HLS request that precedes a 401, caused an outbound request to the JWKS server while holding the manager's write lock. An unauthenticated client could therefore make the server contact the identity provider at will and, while that endpoint was slow or unreachable, stall JWT authentication for everybody. Return "JWT not provided" before touching the JWKS. Also check the JWKS response status code. A non-2xx reply was fed straight to the JSON decoder, so an error page surfaced as a confusing parse error,and an error reply that happened to hold valid JSON was cached as the key set for an hour. Reject it with the status code, as authenticateHTTP already does. Behavior for requests that carry a token against a healthy JWKS server is unchanged.
rewindablereader: fix possible OOM (bluenviron/mediacommon#383) empty datagram packets might cause the allocation of unnecessary entries. Fix that.
h264: revert bluenviron/mediacommon#142 (bluenviron/mediacommon#384) supporting standalone SEI units should not be necessary anymore since these are filtered out at the root (bluenviron/gortsplib#1184). In case of any issue that should arise after this change, open a bug report and we'll adjust things accordingly.
h264: support SEI recovery point in DTS extractor (bluenviron/mediacommon#341) Treat a SEI recovery point message (payload type 6) as a random access point, similarly to a CRA in H265, so the DTS extractor can (re)initialize its state from the first coded frame that follows the recovery point.
API
fix race condition when kicking RTSP sessions (#6260) (#6275)
do not change passwords when their value is "" (#6141) (https://github.com/bluenviron/mediamtx/issues/6287) (#6292) some users call /config/global/get and passits content to /config/global/patch. Since the former now redacts passwords and replaces their value with "", this string is used as an effective password, breaking authentication. Now password values equal to "" are ignored by /config/global/patch, and the original password value is kept unchanged.
fix deadlock when reloading the API (#6280) (#6279) (#6310) When the API is closing, a /config endpoint might be waiting on the server to reply, but the server is busy waiting on all API endpoints to return, causing a deadlock. Fix this by canceling requests from API endpoints to the server when the API is closing.
Media-Over-QUIC
use "catalog" as catalog track name (#6268) Previously, the catalog track name was ".catalog" which is non-standard.
apply path-level rtspUDPReadBufferSize to the first connection (#6291) The RTSP static source built its client before reading the path-level rtspUDPReadBufferSize, so the value only reached the client after a reconnection. Compute it before creating the client, as the MPEG-TS and RTP sources do and as this source did before #5488.
drop standalone H264/H265 trailing SEIs (bluenviron/gortsplib#1184) #3614bluenviron/mediacommon#142#6257bluenviron/mediacommon#382 H264/H265 access units containing a single SEI only, trailing other units, generated by someTP-Link/Tapo cameras, are non-standard and cause several issues in downstream components, including the DTS extractor and the recorder. Previously, the H265 DTS extractor was adjusted to support such units (bluenviron/gortsplib#142) but the problem represented itself with H265. Filter these units out at the root.
complete RTP/JPEG implementation (bluenviron/gortsplib#1187) add support for restart headers, limit support to JPEG types 0 and 1.
RTMP
detect read errors even when the server is writing (#6267) Until now we relied exclusively on write errors to detect broken RTMP connections. Now read errors are checked too.
writer: drain inbound messages (#5776) (bluenviron/gortmplib#132) Some servers periodically send messages even when we're publishing. If they are not read, they fill an internal connectionbuffer and cause periodic disconnections. Now the Writer also drains inbound messages.
reader: support FLV video command frames and skip empty audio messages (bluenviron/gortmplib#128) Someservers (e.g. Wowza Streaming Engine) send a video info/command frame before the first frame: a 2-byte message with frame type 5 and a single command byte (start of seek / end of seek). Video.unmarshal rejected it on length, aborting the whole connection. Add Video.FrameType (key frame, inter frame, command) and Video.Command, deprecate Video.IsKeyFrame in favour of FrameType, and skip command frames in Reader since they carry no data. Some servers (e.g. Wowza Streaming Engine) send an audio message with an empty bodybefore the first frame. allocateMessage rejected it with "not enough bytes", aborting the whole connection. Parse an empty body into an Audio with Codec = 0 and no other field, marshal it back to an empty body, and skip such messages in Reader since they carry no data, like FFmpeg does.
rename message.Video.Type into message.Video.PacketType (bluenviron/gortmplib#138) This is to avoid confusion with the new message.Video.FrameType field.
HLS
muxer: keep EXT-X-MAP in delta updates (bluenviron/gohlslib#397) Delta updates were generated without EXT-X-MAP, although all tags that are not skipped must remain in them. Apple clients stop playback (MoofManifold -16046) at the first delta update that skips a segment after delta updates without skipped segments.
WebRTC
fix panic with specially-crafted WHEP requests (#6301) (#6309) this happened in case of WHEP requests with no media descriptions in the SDP.
Dependencies
Go updated from 1.26 to 1.27
code.cloudfoundry.org/bytefmt updated from v0.90.0 to v0.92.0
github.com/bluenviron/gohlslib/v2 updated from v2.4.5 to v2.4.6
github.com/bluenviron/gortmplib updated from v1.0.3 to v1.0.4
github.com/bluenviron/gortsplib/v5 updated from v5.6.6 to v5.6.7
github.com/bluenviron/mediacommon/v2 updated from v2.9.5 to v2.9.6
github.com/datarhei/gosrt updated from v0.11.1-0.20260812091715-a77b40bb4b76 to v0.12.0
github.com/gin-contrib/pprof updated from v1.5.5 to v1.5.6
github.com/go-git/go-billy/v5 updated from v5.9.1 to v5.9.2
github.com/go-git/go-git/v5 updated from v5.19.2 to v5.19.3
github.com/pion/ice/v4 updated from v4.4.2 to v4.4.5
github.com/pion/interceptor updated from v0.1.48 to v0.1.49
github.com/pion/rtcp updated from v1.2.17 to v1.2.19
github.com/pion/transport/v4 removed
github.com/pion/webrtc/v4 updated from v4.2.20 to v4.2.22
github.com/quic-go/quic-go updated from v0.62.0 to v0.63.0
github.com/pion/datachannel updated from v1.6.2 to v1.6.3
github.com/pion/dtls/v3 updated from v3.1.8 to v3.1.9
github.com/pion/mdns/v2 updated from v2.2.0 to v2.2.2
github.com/pion/sctp updated from v1.11.1 to v1.11.3
github.com/pion/srtp/v3 updated from v3.0.15 to v3.1.3
github.com/pion/stun/v4 updated from v4.0.0 to v4.0.1
github.com/pion/turn/v5 updated from v5.1.0 to v5.1.2
github.com/pjbgf/sha1cd updated from v0.6.0 to v0.7.0
github.com/pion/transport/v5 v5.1.1 added
Security
Binaries are compiled from source code by the Release workflow, which is a fully-visible process that prevents any change or external interference in produced artifacts.
Checksums of binaries are also published in a public blockchain by using GitHub Attestations, and they can be verified by running:
ls mediamtx_* | xargs -L1 gh attestation verify --repo bluenviron/mediamtx
You can verify checksums of binaries by downloading checksums.sha256 and running:
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
Changed
Fixed
JMAP: Creating a MaskedEmail with emailDomain fails with forbidden for every domain when the account has addresses on more than one domain.
Autodiscover: Requests for a response schema other than Outlook's, such as ActiveSync (mobilesync), are answered with the Outlook settings instead of error 601.
IMAP:
LOGIN and AUTHENTICATE with a wrong, expired or unknown app password or API key are answered with an untagged NO, so clients keep waiting for the command to complete until the connection times out.
The failed login that exceeds the maximum number of authentication failures is answered with an untagged NO before the connection is closed.
DKIM:
A rotation moves the active key to retiring even when its successor fails to publish or propagate, so outgoing mail is sent unsigned until a retry publishes the new key. The DNS write failure is also not logged and the task reports success.
Keys created while DNS management was manual, or before DKIM was added to the published records, are never rotated after DNS management becomes automatic. Domains already affected start rotating once a DkimManagement task is created for them.
After switching DNS management from automatic to manual, a due rotation activates a new key that was never published in DNS, so signatures fail verification, and retiring the old key is retried forever.
Spam filter:
Messages with no text line long enough for a Pyzor digest are checked with the digest of empty input and tagged PYZOR.
DNSBL answers with several return codes, such as a Spamhaus ZEN listing in both SBL and PBL, are scored for only the first code returned.
DNSBL lookups that return "not listed" are cached for 24 hours regardless of the zone's negative TTL.
Removing a duplicate training sample of a message reclassified on the same day clears the blob link of the sample that is kept.
MTA: Queue quotas with an empty match expression are never enforced, including the global queue quota created on first start.
RocksDB: The info log (LOG, LOG.old.*) grows without limit because log rotation and retention are left at RocksDB defaults.
WebUI: A blob store read error at startup, such as an S3 authentication failure, stops the web interface from being downloaded.
Fixed script injection in event scripts. With JScript, untrusted values passed to event handlers (such as the logon password in OnClientValidatePassword, or error text and UIDs sent by remote servers) could break out of their string. With either script language, a rule's "Run function" name could carry code. This would allow an attacker to run arbitrary code in the hMailServer process. Values are now escaped, and rule function names may only contain letters, digits and underscores.
If you are unable to install this version, disable JScript execution and do not give end-users access to the hMailServer COM API.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
DNS: PowerDNS Authoritative provider for automatic DNS record management.
Changed
Fixed
Troubleshoot tool: TLSA records are looked up for every MX host, including hosts whose zone is not DNSSEC signed.
Spam filter:
OpenPhish and PhishTank entries containing uppercase characters never match, since message URLs are lowercased while HTTP lookup entries keep their original case. HTTP lookups now match keys case-insensitively.
URL shortener links are followed using the lowercased URL, so case-sensitive short links resolve to the wrong destination or not at all.
Incremental training never advances its position past the first run, so every retained sample added since then is trained again, and counted again in the reservoir, on each run until it expires.
Updating the rules only adds new objects, so upstream changes to existing rules, DNSBL servers, HTTP lookups, lookup keys and file extensions never reach an existing installation.
Updating the rules reports success when objects fail to import, or when a configuration error stops the updated settings from being activated.
JMAP:
A PushSubscription created within the verification rate limit window of another one on the same account never receives its PushVerification, since the blocked verification is dropped instead of being sent once the window expires.
A push notification retried after a failed delivery can report an older state than a change queued during the failed attempt, since the older state changes are merged last and overwrite the newer ones.
Changes made while a push request is in flight are not delivered until the next change reaches the same subscription, since a successful delivery cancels the pending retry.
The VAPID aud claim is derived from a hand-written parse of the push URL, so a crafted push URL can make the server sign a token for a push service other than the one the request is sent to.
Email/import rejects a blobId that refers to a Blob/upload creation id in the same request ("#u0") with Invalid blob id..
Email/set with a full mailboxIds object identical to the current mailboxes, together with a keyword change, stores the message with IMAP UID 0, so IMAP clients stop seeing it.
MTA:
A node without the outboundMta role stops replying to DATA and to JMAP submissions once about 1024 messages have been queued on it.
MX records are resolved through the DNSSEC-validating resolver even when DANE is disabled.
A DATA stage Sieve script does not see headers added by milters or MTA hooks, and discards every milter and MTA hook change when it edits the message.
MySQL: Range deletions and search index removals start with a single unbounded DELETE and switch to chunks only after a timeout.
IMAP: COPY and MOVE fail with NO [CONTACTADMIN] when another session changes the same message at the same time.
Autodiscover: Implicit TLS ports (993, 995, 465) are advertised with <Encryption>TLS</Encryption>, which Outlook reads as STARTTLS.
HTTP: Idle keep-alive connections are never closed.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
Expressions: bit_and function.
Changed
Fixed
MTA:
A mailing list whose recipients include another mailing list is accepted at RCPT TO and then rejected at local delivery with 550 5.5.0 Mailbox not found.
DMARC aggregate reports carry two spf elements per record and the version element of a DMARC aggregate report is written as 1 instead of 1.0.
DSNs generated for an alias rewrite or a list expansion emit a doubled addr-type in Original-Recipient (rfc822;rfc822;user@example.org).
DSNs that cannot be written to the store are discarded, the recipients are flagged as notified and the original message is removed from the queue, losing both the bounce and the message.
POP3:
TOP msg n counts the n lines from the first byte of the message instead of from the first byte of the body.
A message whose very first line begins with . is not byte-stuffed.
Spam filter: Moving or copying a message from one account into another creates no training sample, so the classifier never learns from it.
Sieve: envelope "orcpt" yields the bare address for an ORCPT supplied over SMTP. It now carries the addr-type prefix in every case, as required by RFC 6009.
ACME: The _acme-challenge TXT records published for a DNS-01 authorization are never removed.
DNS: The DNSSEC resolver queries a single nameserver at a time, working around a hickory-resolver race that cancels the TCP retry when two nameservers return a truncated response in parallel.
Troubleshoot tool:
MX records are resolved through the DNSSEC-validating resolver, matching the resolver used by the delivery path.
A TLSA lookup that fails or returns bogus records stops the delivery attempt for that host, instead of continuing without DANE.
OIDC: Bearer tokens that carry no email, preferred_username or upn claim are always authenticated against the default directory.
Meilisearch: A confirmation timeout is treated as a failed write even when failOnTimeout is disabled, so an index whose batches take longer than pollInterval x maxRetries never completes an indexing task and resubmits the same batch indefinitely.
WebUI: A failed update no longer takes an Application offline.
FoundationDB: The cached read version is invalidated when any broadcast is received from another node.
Redis:
On a cluster, the rate limiter and the blob upload quota issue INCR and EXPIRE as a MULTI/EXEC transaction, whose MOVED redirects collapse into a single EXECABORT that never refreshes the slot map.
A connection that fails because it is addressing the wrong server is returned to the pool and reused, since the recycle check only issues PING.
require browser POST requests to have an allowed origin (#6238) Browsers can send POST requests without performing any preflight request and without checking whether the request origin is present in Access-Control-Allow-Origin. Intercept these requests and check server-side whether the origin is allowed, blocking the request if it is not.
conf: fix comparison and sharing of the default authInternalUsers (#6205)
pmp4, fmp4: restore check on inbound H265 SPS (bluenviron/mediacommon#374) A valid H265 SPS is required for marshaling back an MP4, so we have to resume checking them.
pmp4: support tracks without edts (bluenviron/mediacommon#375) The edit box is optional (ISO/IEC 14496-12, 8.6.5), but tracks without it were rejected with "unexpected box 'mdia'".
pmp4: return seek errors from GetPayload (bluenviron/mediacommon#376) GetPayload checked the wrong variable after seeking, therefore a failed seek was ignored and the payload was read from the current position of the reader.
pmp4, fmp4: correctly fill HvcC NumTemporalLayers / TemporalIdNested from SPS (bluenviron/mediacommon#379)
rtpmjpeg: reject reserved quantization value 127 (bluenviron/gortsplib#1161) RFC 2435 reserves quantization values 100-127, but the guard used Quantization < 127
make the server manage sessions (#6239) Sessions are now managed by the HLS server, detaching their lifecycle from muxers. This also comes with a performance improvement, since finding a session now traverses a single mutex, and not two channels like before.
return 404 when a session is valid but muxer is closed (#5736) (#6240) HLS sessions are now kept open for up to 30 seconds after a muxer is closed. This allows the server to handle incoming requests belonging to these sessions and to classify then as 404 rather than 401.
change error returned in case session is not found (#6241) match the RTSP behavior and return a "session not found" message rather than an "authentication error", which is reserved for creating sessions, not for using them.
fix AV1 getting stuck by stripping padding (#5632) (#5774) (#6242) AV1 OBU padding is generated by several encoders. Stripping it has been demonstrated to solve most situations in which browsers get stuck when playing such streams.
fix stuck decoding with DJI drones (#5221) (#6051) (#6243) Streams from DJI drones contain SEI units with payload type 5 and a specific UUID that seem to cause stuck videos on Chrome and Firefox. Filter out these units.
code.cloudfoundry.org/bytefmt updated from v0.88.0 to v0.90.0
github.com/MicahParks/keyfunc/v3 updated from v3.8.1 to v3.8.2
github.com/abema/go-mp4 updated from v1.7.1 to v1.7.3
github.com/bluenviron/gohlslib/v2 updated from v2.4.4 to v2.4.5
github.com/bluenviron/gortmplib updated from v1.0.2 to v1.0.3
github.com/bluenviron/gortsplib/v5 updated from v5.6.5 to v5.6.6
github.com/bluenviron/mediacommon/v2 updated from v2.9.4 to v2.9.5
github.com/gin-contrib/pprof updated from v1.5.4 to v1.5.5
github.com/matthewhartstonge/argon2 updated from v1.5.7 to v1.6.3
github.com/pion/ice/v4 updated from v4.4.1 to v4.4.2
github.com/pion/interceptor updated from v0.1.47 to v0.1.48
github.com/pion/sdp/v3 updated from v3.0.19 to v3.0.20
github.com/pion/transport/v4 updated from v4.1.0 to v4.1.1
github.com/pion/webrtc/v4 updated from v4.2.19 to v4.2.20
golang.org/x/crypto updated from v0.55.0 to v0.57.0
golang.org/x/net updated from v0.58.0 to v0.59.0
golang.org/x/sync updated from v0.22.0 to v0.23.0
golang.org/x/sys updated from v0.47.0 to v0.48.0
golang.org/x/term updated from v0.45.0 to v0.46.0
github.com/bytedance/gopkg updated from v0.1.3 to v0.1.4
github.com/bytedance/sonic updated from v1.15.0 to v1.15.2
github.com/bytedance/sonic/loader updated from v0.5.0 to v0.5.1
github.com/cloudwego/base64x updated from v0.1.6 to v0.1.7
github.com/gabriel-vasile/mimetype updated from v1.4.12 to v1.4.13
github.com/gin-contrib/sse updated from v1.1.0 to v1.1.1
github.com/go-playground/validator/v10 updated from v10.30.1 to v10.30.3
github.com/goccy/go-json updated from v0.10.5 to v0.10.6
github.com/klauspost/cpuid/v2 updated from v2.3.0 to v2.4.0
github.com/mattn/go-isatty updated from v0.0.20 to v0.0.23
github.com/pelletier/go-toml/v2 updated from v2.2.4 to v2.4.3
github.com/pion/dtls/v3 updated from v3.1.5 to v3.1.8
github.com/pion/mdns/v2 updated from v2.1.0 to v2.2.0
github.com/pion/srtp/v3 updated from v3.0.13 to v3.0.15
github.com/pion/stun/v3 removed
github.com/pion/turn/v5 updated from v5.0.13 to v5.1.0
go.mongodb.org/mongo-driver/v2 updated from v2.5.0 to v2.8.0
golang.org/x/arch updated from v0.22.0 to v0.29.0
golang.org/x/text updated from v0.41.0 to v0.42.0
google.golang.org/protobuf updated from v1.36.10 to v1.36.11
github.com/pion/stun/v4 v4.0.0 added
hls.js updated from v1.7.2 to v1.7.3
Security
Binaries are compiled from source code by the Release workflow, which is a fully-visible process that prevents any change or external interference in produced artifacts.
Checksums of binaries are also published in a public blockchain by using GitHub Attestations, and they can be verified by running:
ls mediamtx_* | xargs -L1 gh attestation verify --repo bluenviron/mediamtx
You can verify checksums of binaries by downloading checksums.sha256 and running:
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
Changed
Fixed
WebDAV: PROPFIND responses do not declare the namespace of every requested property on the DAV:multistatus element.
MTA:
SMTP session state obtained before STARTTLS is discarded once the TLS handshake completes, as required by RFC 3207.
Inbound throttle, outbound throttle and queue quota match expressions evaluate their if/then conditions.
DMARC: Messages without an aligned SPF or DKIM pass from a domain that publishes a DMARC policy are reported as fail instead of none in logs.
DMARC: A temporary DNS error on an SPF or DKIM check whose identifier aligns with the author domain produces temperror instead of fail.
DMARC: A policy record without a p tag is only applied, as p=none, when it contains a rua tag, even if it declares sp or np.
iMIP: Notification subjects and bodies omit the event start date, time and timezone for recurring events.
OIDC: Discovery is retried for 30 seconds before the directory is marked as unavailable, so a provider that is still starting up no longer requires a restart.
WebUI: The cached bundle of an Application is discarded when its resourceUrl changes or the record is deleted, so the next unpack fetches the new resource.
DNS: Append . to MX record hostnames.
Recovery mode: Automatic IP banning is disabled during recovery and initial setup.
FoundationDB: The cached read version is invalidated when a queue refresh or registry change broadcast is received.
Sieve: Messages filed with fileinto by a user script are treated as ham and are no longer moved to Junk when classified as spam.
IMAP:
SELECT, EXAMINE and STATUS on a shared mailbox require the r (read) right.
AUTHENTICATE is refused on clear-text connections unless allowPlainTextAuth is enabled, and the greeting and CAPABILITY response advertise LOGINDISABLED instead of the AUTH= mechanisms in that case.
IMAP, POP3, ManageSieve: Partial commands received before STARTTLS or STLS are discarded once the TLS handshake completes.
Network: Inbound TLS handshakes, both implicit and STARTTLS, are aborted after the listener's tlsTimeout, releasing the connection slot.
OAuth: A random key is used when the OIDC provider's encryptionKey or HMAC signatureKey cannot be read, instead of an empty key.
LDAP: The {local} and {domain} filter placeholders are escaped.
JMAP:
CalendarEvent/set stores useDefaultAlerts per user, and CalendarEvent/get returns false when it was never set.
CalendarEvent/get returns null for baseEventId unless the id is a synthetic recurrence instance.
CalendarEvent/get returns null for requested recurrenceRule and recurrenceOverrides properties on synthetic recurrence instances.
CalendarEvent/get and ContactCard/get return only id when the properties argument is an empty list, instead of every property.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
Changed
Sieve: Messages generated by user scripts are DKIM signed through the new SieveUserInterpreter.dkimSignDomain setting, which defaults to the account's own domain.
Fixed
JMAP:
CalendarEvent/set requests that ask for scheduling messages are rejected with a forbidden error when the account cannot send them.
Calendar/get and AddressBook/get return every property when the properties argument is omitted or null.
EventSource ping events advertise the interval in seconds rather than milliseconds.
Calendar synthetic ids returned when expanding recurrences identify an occurrence by its recurrence id.
IMAP: Every command in a pipelined STATUS or FETCH batch receives its tagged completion, instead of the first failing command dropping the responses for all commands queued behind it.
WebDAV: Accounts without a storage quota no longer advertise a 4 GiB limit in DAV:quota-available-bytes.
MTA: Inbound DMARC and TLS aggregate reports that a reporter sends more than once are imported again as a duplicate entry.
Spam filter: Domain and URL blocklists are queried only for text written as a link.
iTIP: Detaching an occurrence that the recurrence rule already generates is sent as a METHOD:REQUEST carrying the RECURRENCE-ID instead of a METHOD:ADD.
Sieve: fileinto :specialuse and specialuse_exists accept special-use attributes in the \Trash form.
LDAP: Active Directory servers that answer an unauthenticated bind (a non-empty DN with a zero-length password) with success no longer authenticate accounts without a password.
Network: Listeners bound to the unspecified IPv6 address ([::]) fall back to IPv4 when socket creation fails with EPROTONOSUPPORT.
OpenTelemetry: log exporter does not include the parent span's attributes.
use internal/core/VERSION as single source of truth to get version (#6119)
httpp: propagate Flush() through the response writer wrappers (#6126) handlerLogger's responseRecorder and handlerWriteTimeout's writeTimeoutWriter both wrap http.ResponseWriter but implement neither http.Flusher nor Unwrap(). Any handler that flushes mid-response has that flush silently dropped, and its output only reaches the client when the handler returns. That defeats the stated purpose of writeTimeoutWriter, whose own comment says it exists so one can "write long responses, splitted in chunks, without causing timeouts": the deadline is reset per Write(), but nothing ever leaves the buffer until the end. Found while adding a server-sent-events endpoint: events written and flushed at t=0.4s onlyreached the client at t=2.2s, when the handler returned. Adding Flush() plus Unwrap() (so http.ResponseController can reach through) fixes both. The new test drives a real listener throughboth wrappers and reads while the handler is still blocked, so it fails on the current code by hitting the read deadline.
support setting read buffer size on macOS (#6128) (#6131)
drop support for Debian Bullseye (#6137) Bullseye reached EOL on 31st august 2026.
emit "closed" log message when a server is closed (#6152)
fix external command exit code reporting on non-Windows platforms (#6155)
metrics: add 'pos' property to forward_dests (#6183)
metrics: deprecate the 'remoteAddr' property (#6174) Metrics are meant to provide aggregate statistics, not per-peer properties, that can be accessed through the API. The property is still served to keep backward compatibility.
metrics: add 'type' to forward dests, deprecated 'protocol' (#6170) 'type' is aligned with the terminology used in the rest of the server.
metrics, pprof: set default Allow-Origins to empty list (#6165) This prevents cross-site attacks to the API with the default configuration.
always-available: support disabling recording when the source is offline (#6182) Add alwaysAvailableRecorded parameter that controls whether recording happens during offline periods for always-available paths. When set to false, recording only starts when a real source is connected.
allow sources and forward destinations with an empty password (#6193) Restore the behavior introduced in #395 and accidentally reverted by #5779, which factored the RTMP/HLS credential check into validateURL() and applied it to RTSP sources too. A username with an explicitly empty password is valid per RFC 3986 and is required by some legacy cameras whose password cannot be set. It is now accepted for every scheme handled by validateURL(), including forward destinations. A username without password or a password without username is still rejected,with a clearer error message pointing to the right syntax.
fix wrong validation of HTTP allowed origins (#6198) An allowed origin with wildcards was making the server accept origins with a different scheme. This is fixed.
fix HTTP allowed origins dots being interpreter as any character (#6199) A dot in an HTTP allowed origin now requires a corresponding dot in an incoming origin.
serve Origin: Vary header with Access-Control-Allow-Origin (#6200) This prevents CDNs from caching the Access-Control-Allow-Origin header regardless of the origin.
stop returning wildcard in Access-Control-Allow-Origin (#6201) if domain is allowed, return the domain, otherwise nothing.
pmp4, fmp4: stop validating inbound H264/H265 SPS (bluenviron/mediacommon#366) Validating SPS adds latency and is generally redundant.
h265: fix DTS extraction of streams with temporal sub-layers (bluenviron/mediacommon#368) The picture-type switches in the H265 DTS extractor accepted only TRAIL_N/TRAIL_R/RASL_N/RASL_R, so an access unit whose slices are TSA_N/TSA_R/STSA_N/STSA_R (Rec. ITU-T H.265, table 7-1, types 2-5) fell through to "access unit doesn't contain an IDR or non-IDR NALU" and the stream could never be recorded. TSA and STSA pictures use the same slice segment header syntax as TRAIL pictures, so handle them in the same way.
API
add 'type' to forward dests, deprecated 'protocol' (#6170) 'type' is aligned with the terminology used in the rest of the server.
set default Allow-Origins to empty list (#6165) This prevents cross-site attacks to the API with the default configuration.
deprecate 'conf' field of forward destinations (#6175) configuration is already available through the confName property of the path.
refactor endpoint names (#6172) group endpoints by category (i.e. /hls/muxers, /hls/sessions), rename /forward/* into /forward-dests/*, use kebab-case. Backward compatibility is preserved by still serving old endpoints.
Media-Over-QUIC
rename boolean header fields (#6148) rename Properties into HasProperties, FirstObject into IsFirstObject
close server streams when no longer in use (#6151) Explicitly close bidirectional and unidirectional streams that were previously keep open for the entire duration of the session. Note that closing a stream in QUIC/WebTransport means telling the peer that "we are not sending data anymore", while still allowing inbound data.
impose a 100MB limit on reordered bytes per session (#6194) previously, the limit was per-track. This decreases the probability of incurring in an out-of-memory situations.
demux MPEG-TS from static source pull (#6181) When rtspDemuxMpegts is set, a pulled single MP2T track is demuxed into elementary streams, matching RTSP publishers, so HLS can attach.
rtpklv: do not reuse buffer of returned KLV units (bluenviron/gortsplib#1154) Decode() returns a slice that points to the decoder's internal buffer, and the next call to Decode() overwrites that same array in place. A caller that holds the previous unit - for instance while it is being recorded or forwarded on another goroutine - ends up reading the bytes of the next unit, or a torn mix of both when the sizes differ. Clear the buffer instead of truncating it, so each unit gets its own array. This is what the h264, h265, av1, vp8 andmpeg1video decoders already do, at the cost of one allocation per unit.
auth: support qop=auth in digest client (bluenviron/gortsplib#1074) (bluenviron/gortsplib#1155) The client never looked at the qop directive of a digest challenge and always computed the legacy RFC 2069 response, so servers that require qop="auth" - as most RFC 7616 implementations do - rejected every request with a 401. Parse qop in WWW-Authenticate, and when the server offers "auth", send qop, cnonce and nc and compute the response as H(HA1:nonce:nc:cnonce:qop:HA2) (RFC 7616, section 3.4.1). The nonce count increases at every request, as required. Challenges without qop keep the previous behavior. auth-int is not implemented. Also copy the opaque value of the challenge into the Authorization header, which RFC 7616 section 3.3 says the client should return unchanged.
rtpav1: do not set Y/Z bits when no OBU bytes were written (bluenviron/gortsplib#1153) When the remaining space in a packet is too small to hold any byte of thenext OBU, the encoder writes nothing but still marks the current packet with Y=1 and the following one with Z=1. The AV1 RTP payload format (v1.0, section 4.4) requires both bits to be 0 unless an OBU element is actually fragmented across the two packets. Receivers that honor the bits therefore join two complete OBUs into one: this happens with the decoder in this package, with pion/rtp and with ffmpeg. It is reachable whenever an earlier OBU leaves exactly 0 free bytes (or 0-2 when the next OBU is size-prefixed), so it affects streams that are repacketized, e.g. non-RTSP sources read over RTSP in mediamtx.
code.cloudfoundry.org/bytefmt updated from v0.85.0 to v0.88.0
github.com/bluenviron/gohlslib/v2 updated from v2.4.3 to v2.4.4
github.com/bluenviron/gortmplib updated from v1.0.1 to v1.0.2
github.com/bluenviron/gortsplib/v5 updated from v5.6.4 to v5.6.5
github.com/bluenviron/mediacommon/v2 updated from v2.9.3 to v2.9.4
github.com/matthewhartstonge/argon2 updated from v1.5.6 to v1.5.7
github.com/pion/webrtc/v4 updated from v4.2.18 to v4.2.19
github.com/quic-go/quic-go updated from v0.61.0 to v0.62.0
github.com/quic-go/webtransport-go updated from v0.12.0 to v0.13.0
github.com/stretchr/testify updated from v1.12.0 to v1.12.1
github.com/dunglas/httpsfv updated from v1.1.0 to v1.1.1
github.com/pion/stun/v3 updated from v3.1.6 to v3.1.7
github.com/pion/turn/v5 updated from v5.0.12 to v5.0.13
gopkg.in/yaml.v3 removed
go.yaml.in/yaml/v3 v3.0.5 added
github.com/bluenviron/mediamtx-rpicamera updated from v2.8.0 to v2.9.0
hls.js updated from v1.7.0 to v1.7.2
Security
Binaries are compiled from source code by the Release workflow, which is a fully-visible process that prevents any change or external interference in produced artifacts.
Checksums of binaries are also published in a public blockchain by using GitHub Attestations, and they can be verified by running:
ls mediamtx_* | xargs -L1 gh attestation verify --repo bluenviron/mediamtx
You can verify checksums of binaries by downloading checksums.sha256 and running:
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
System for Cross-domain Identity Management (SCIM) v2 (Enterprise):
JMAP: CalendarEvent/set support for updating and deleting synthetic ids (#2925).
Calendar:
Conference links in calendar invites and email alarms.
Translations for Arabic, Brazilian Portuguese, Bulgarian, Chinese Simplified, Chinese Traditional, Croatian, Czech, Finnish, Hebrew, Hindi, Hungarian, Indonesian, Japanese, Korean, Lithuanian, Norwegian BokmΓ₯l, Persian, Romanian, Russian, Slovak, Slovenian, Thai, Turkish, Ukrainian and Vietnamese.
Changed
Calendar: Updated HTTP RSVP page.
Fixed
DANE:
TLSA records are looked up whenever the MX RRset is signed, even when the MX host's own zone is not.
Mandatory DANE failures are permanent rather than temporary, bouncing messages that should be delayed.
Valid but unusable TLSA records fall back to the configured TLS strategy, permitting cleartext delivery where TLS is required.
S3: Fix outdated upstream af-south-1 region configuration.
Setup wizard: SQL directories set to use the main data store are now validated against the data store being configured.
CardDAV: Delete default address book id when deleting the default address book.
Redis: Sentinel deployments configured with rediss:// URLs now connect to the master over TLS instead of silently falling back to cleartext.
Email: Generated Message-ID headers use the hostname of the node that built the message instead of the configured server hostname.
MTA:
Do not send DMARC reports to local domains.
Messages addressed to an inboundReportAddresses match are only discarded when they actually contain a report (#1088).
Directory: Impersonation using the recovery admin fails when the impersonated account has not logged in before (LDAP and SQL directories).
WebUI: Failed logins open the browser's native credential prompt.
Cluster: Expired node id leases are released periodically rather than only during startup, so entries for removed nodes no longer remain Stale or Inactive indefinitely.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
WebUI: oauthClientId setting in Application, which allows the WebUI to use a different OAuth client than the default.
Sieve: env.spam.score and env.spam.is_spam variables, which expose the spam filter result to system scripts running at the DATA stage.
CalDAV: vCardVersion setting in AddressBook, which allows the default vCard version to be specified when the client does not request a specific version.
Changed
MySQL & MariaDB: Key columns are now VARBINARY(255) with a full-length primary key instead of TINYBLOB. Note: Existing deployments should run, once per table, for each of the tables a, d, e, f, g, h, j, k, l, m, n, o, p, q, r, s, t, u, w, x and y the command ALTER TABLE a MODIFY k VARBINARY(255) NOT NULL;.
Fixed
ACME:
Order and authorization failures are never logged, so an order rejected by the CA.
An order rejected by the CA marks the renewal task as permanently failed.
CalDAV:
Attendee addresses whose mailto: URI percent-encodes a full name-addr are silently dropped from the scheduling snapshot.
Attendees whose calendar user address cannot be parsed should be flagged with SCHEDULE-STATUS=3.7.
The RSVP link in an iMIP invitation stamps PARTSTAT on the organizer's copy of the event only, leaving a local attendee's own copy at NEEDS-ACTION and sending the organizer no reply.
MKCALENDAR, MKCOL and PROPPATCH store the display name, description, time zone and the other per-user properties under the authenticated account rather than the account that owns the collection.
Directory:
An empty column, attribute or claim returned by an external directory is synchronized as an empty string rather than a missing value.
/api/discover splits the account name on @ without accounting for the % master user separator or the recovery administrator.
FoundationDB: Older chunked entries are not deleted.
IMAP: SETACL and DELETEACL fail to resolve an identifier spelled with uppercase characters.
iMIP: Invitations, replies and cancellations reference a TZID parameter with no matching VTIMEZONE component whenever the event was stored without one.
JMAP:
AddressBook/get: A new account's default address book is never recorded.
Email/get and Email/parse with fetchAllBodyValues return body values only for the parts listed in textBody or htmlBody, omitting every other text/* part in bodyStructure.
Email/set writes display names as an RFC 2047 encoded-word wrapped in a quoted-string, which RFC 2047 forbids.
Mailbox/set, AddressBook/set and Calendar/set store isSubscribed and the other per-user properties under the authenticated account rather than the account named in the request.
Principal/query returns no results when the name or email filter is spelled with uppercase characters.
FileNode/set: File nodes created over JMAP are returned with a <D:href> holding the raw name over WebDAV.
Meilisearch:
Queries return at most 1000 results, as the maxTotalHits pagination setting is left at the Meilisearch default.
Searches combining several terms return documents that match only some of them.
A task confirmation timeout is reported as a success when failOnTimeout is disabled.
Import: --import always aborts with the target database already containing data in the key range being imported.
MTA:
A domain catchAllAddress pointing to a mailing list or a sub-addressed mailbox is accepted at RCPT TO and then rejected at local delivery with 550 5.5.0 Mailbox not found.
is_local_address() and is_local_domain() expression functions do not match an address or domain spelled with uppercase characters.
Relay routes are rejected with host resolves loopback address, which prevents relaying through a local proxy or tunnel.
MySQL, MariaDB & PostgreSQL: Range scans, range deletions and store purges run as a single unbounded statement, so on servers that enforce a statement timeout they abort on large accounts and tasks such as account deletion can never complete.
Network: local_port and local_ip report the address Stalwart is bound to rather than the address the client connected to when the connection arrives through a trusted proxy.
Task manager:
totalDeadline is not enforced on tasks that fail with a specific retry time.
Indexing tasks are dropped after maxAttempts failures, so a search store that is unavailable or overloaded leaves messages permanently missing from the index.
Indexing tasks are dropped when the document metadata read returns no data, which can happen on SQL read replicas that have not yet caught up with the primary.
The DNS management task republishes the DKIM records of retired keys that the DKIM rotation task had already removed from the zone.
Sieve: spamtest returns only 1 or 10 (and spamtest :percent only 0 or 100), so scripts cannot act on intermediate spam scores.
Spam filter: MIME_BAD is tagged whenever the declared Content-Type of an attachment is not byte identical to the type detected from its magic bytes.
prevent spamming of 'stream is closed' error during shutdown (#6062)
impose a minimum value to clock rate of always-available tracks (#6086) Clock rates below 10 caused the emission of empty samples. Fix the issue by imposing a minimum value of 8khz, that rises to 22khz in case of AAC.
fix race condition during sub-stream creation (#6075) (#6095) When a stream with always-available turned on switches from offline to online, or from a publisher to another, the reader mutex was not acquired during writing of codec parameters. This is now fixed.
restore ability to run the server in a read-only file system (#6098) This was temporarily lost after the introduction of the native MoQ QUIC listener.
fix deadlock when changing configuration through file and API (#6077) (#6101) When changing configuration in parallel by editing the configuration file and calling the API, the server could get into a deadlock that prevented any further action. This is fixed.
change default value of authHTTPExclude (#6103) by default, do not exclude any action from HTTP authentication. Old value triggered several security warnings.
add destFingerprint parameter (#6106) this allows to validate self-signed certificates of forward destinations.
pmp4: fix panic in case of bad input (bluenviron/mediacommon#354) the stsc box was not checked properly. This is now fixed.
pmp4: fix panic in case of bad input (bluenviron/mediacommon#355) The parser was not checking that the MP4 was properly sending addresses of samples, resulting in samples with invalid addresses. This is now fixed.
limit maximum amount of published tracks (#6087) this prevents clients from consuming an excessive amount of memory.
make /moq URL suffix optional (#6107) In order to establish a MoQ session with WebTransport, a /moq suffix was required until now. This is now optional in order to allow connecting to the server with the standard MoQ URL format.
impose maximum size on pending reordered bytes (#6112) Decrease the maximum memory that clients can take by imposing a maximum size of 100MB on the pending reordered bytes.
RTSP
accept relative digest URI (bluenviron/gortsplib#1118) RFC 2617 section 3.2.2 allows the digest URI to be either an absolute URI or a relative path. Some clients use the latter, which was rejected with "wrong URL" since urlMatches only accepted an exact match against the absolute request URL.
send initial RTCP sender report without waiting for a period (bluenviron/gortsplib#1052) (bluenviron/gortsplib#1111) (bluenviron/gortsplib#1120) Reports were emitted only on the ticker, so the first one arrived Period after Initialize (10s by default) and later still when no RTP packet had been sent by that first tick, since report() returns nil until then and the next opportunity is another Period away.
ensure that decoders can produce only output that does not crash encoders (bluenviron/gortsplib#1123)
inform about authentication failures (#5657) (#6072) Reply with NetStream.Play.Failed or NetStream.Publish.Unauthorized when a client is not authorized to play or publish. This makes clients like OBS to stop recreating the connection in case of authentication failures.
prefer hls.js on iOS too (#6090) In the embedded HLS reader, use hls.js on iOS, that was previously disabled due to compatibility issues that should have been solved.
unlock the session-in-query+iOS combination (#6088) this was previously blocked because the session in query was meant to be dynamic, therefore incompatible with static playlists required by iOS. It is not anymore, so we can support that.
stop using cookies with plain HTTP (#6089) in case of plain HTTP, fall back to query parameters, which are safer than HTTP cookies because they are not shared between different pages/domains, although they are visible in the URL.
improve performance by ignoring mDNS candidates (#4963) (#6064) mDNS candidates sometimes require a large CPU portion, they are not involved in any connectivity method mentioned in the documentation, they work in local networks only.
SRT
apply UDP read buffer size from configuration (#6069) Use upstream datarhei/gosrt's ListenerControl config field (datarhei/gosrt#144) to set SO_RCVBUF on the SRT listener's UDP socket.
RPI Camera
fix crash when secondary stream is enabled (#6060) (#6061)
prevent invalid MJPEG sizes (#6080) width and height of MJPEG frames must be multiple of 8 and less than 2048, otherwise they cannot be routed with RTP/RTSP.
Dependencies
code.cloudfoundry.org/bytefmt updated from v0.83.0 to v0.85.0
github.com/alecthomas/kong updated from v1.16.0 to v1.16.1
github.com/asticode/go-astits updated from v1.15.0 to v1.16.0
github.com/bluenviron/gohlslib/v2 updated from v2.4.2 to v2.4.3
github.com/bluenviron/gortmplib updated from v1.0.0 to v1.0.1
github.com/bluenviron/gortsplib/v5 updated from v5.6.3 to v5.6.4
github.com/bluenviron/mediacommon/v2 updated from v2.9.2 to v2.9.3
github.com/datarhei/gosrt updated from v0.11.0 to v0.11.1-0.20260812091715-a77b40bb4b76
github.com/pion/ice/v4 updated from v4.4.0 to v4.4.1
github.com/pion/transport/v4 updated from v4.0.2 to v4.1.0
github.com/stretchr/testify updated from v1.11.1 to v1.12.0
golang.org/x/crypto updated from v0.54.0 to v0.55.0
golang.org/x/net updated from v0.57.0 to v0.58.0
github.com/davecgh/go-spew removed
github.com/pion/srtp/v3 updated from v3.0.12 to v3.0.13
github.com/pmezard/go-difflib removed
golang.org/x/text updated from v0.40.0 to v0.41.0
hls.js updated from v1.6.16 to v1.7.0
Security
Binaries are compiled from source code by the Release workflow, which is a fully-visible process that prevents any change or external interference in produced artifacts.
Checksums of binaries are also published in a public blockchain by using GitHub Attestations, and they can be verified by running:
ls mediamtx_* | xargs -L1 gh attestation verify --repo bluenviron/mediamtx
You can verify checksums of binaries by downloading checksums.sha256 and running:
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
Reporting: inboundReportMaxSize setting, which bounds the size of a decompressed inbound DMARC or TLS report (default 25MB).
RocksDB: cacheSize setting, which bounds the total memory shared by the block caches of every column family (default 128MB).
Changed
ASN & GeoIP: Default data source URLs now point at the ip-location-db GitHub releases, as the previously used npm packages are no longer updated. Existing installations keep their configured URLs and should update them following the ASN and GeoIP documentation.
JMAP: Identity/get keeps identities in sync with the account's e-mail addresses.
MTA: Queue scheduler no longer rescans the queue from the earliest pending event and coalesces bursts of delivery completions into a single scan.
RocksDB:
Column families are tuned for the access pattern of the data they hold.
Range iteration uses bounded iterators and no longer reads values when only keys were requested.
Fixed
JMAP:
Setting uploadTtl to 1ms triggers panic.
CalendarEvent/set does not assign organizerCalendarAddress nor send scheduling messages when an event is created with participants.
CalendarEvent/get omits isOrigin when it is listed explicitly in properties.
CalendarEventNotification/changes and FileNode/changes reject with cannotCalculateChanges the state that /get returned for an account with no change history.
CalendarEvent/set and ContactCard/set do not write a vanished tombstone for the previous CalDAV/CardDAV href when calendarIds or addressBookIds moves an item between collections.
CalDAV: Attendee addresses that percent-encode a display name into the mailto: URI are queued verbatim.
Calendar: Recurring events disappear from CalDAV time-range REPORTs and JMAP CalendarEvent/query results a few years after their first occurrence.
WebDAV:
When a file node references a parent folder that no longer exists, any request on a file collection panics.
MOVE on a folder honors a Depth header of 0 or 1 instead of always moving the whole subtree.
MTA:
DSN bounces are emitted with a malformed Message-ID wrapped in doubled angle brackets.
Delivery to any MX host whose name is an IDN A-label fails permanently.
Queue strategy and quota expressions that branch on source never match at enqueue.
MTA-STS:
Policies in testing mode are enforced, turning any TLS error into a permanent failure.
mx patterns published as U-labels never authorize the MX host they name.
DMARC:
Alignment compares identifiers in their A-label form.
External reporting addresses published as U-labels are rejected as unauthorized.
Spam filter:
Some rules misfire on internationalized addresses when the envelope and the headers spell the same domain in different label forms.
Punycode labels that do not re-encode to the label they came from are no longer decoded.
WebPush: Validate push URL and use application/octet-stream as Content-Type for encrypted payloads.
Directory:
Local group membership is cleared when the external directory is configured with a group claim or attribute that it does not return.
LDAP: Directories that store aliases as additional values of the primary address attribute provision no aliases.
Mail addressed to a domain alias is rejected with 550 Relay not allowed, unless the domain's primary name happened to be resolved earlier and is still cached.
RocksDB: bufferSize setting was applied to the unused default column family and had no effect.
Sieve: include statements fail to find system and user global scripts whose name contains uppercase characters.
Task manager: totalDeadline is measured from the time a task was created instead of its first failed attempt.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
WebDAV: Range and If-Range header support on file downloads (RFC 7233) (#2377).
Spam filter: url_original expression variable for Url rules.
Changed
Memory allocator: Replaced the unmaintained jemallocator crate with tikv-jemallocator (contributed by @checkraisefold).
ACME registry: Use description as label property.
Fixed
MTA:
Certificates for domains publishing an enforcing MTA-STS policy are always validated, even in the fallback TLS strategy.
DSN delivery date uses wrong timestamp.
FUTURERELEASE HOLDUNTIL uses Unix timestamps instead of RFC 3339 date-times.
JMAP:
EmailSubmission/query filtering on undoStatus contradicts EmailSubmission/get, reporting held FUTURERELEASE submissions as final instead of pending.
EmailSubmission/get requests without an ids argument iterates the wrong index.
CardDAV: Accept: text/vcard version negotiation is ignored whenever another parameter such as q or charset follows version=.
Calendar: Server-side scheduling messages place the text/calendar part outside the multipart/alternative and disposed as an attachment.
Sharing: Accounts holding the impersonate permission never have their ACL grants collected, so shared items are never listed in JMAP sessions, CalDAV/CardDAV discovery or IMAP.
IMAP:
COPY/MOVE into a shared folder fails with NO [ALREADYEXISTS] when the destination account already holds the message, leaving the message in the source mailbox and clients in a retry loop.
BODYSTRUCTURE and ENVELOPE return MIME parameters, Content-Description, subjects and display names as raw UTF-8 even to sessions that never enabled UTF8=ACCEPT.
support forwarding streams natively (#5558) It is now possible to define forward destinations for each path configuration. For each destination, the server will create a client that will forward the stream to the intended destination. Supported protocols are RTSP, RTMP, SRT. API and metrics have also been improved to allow monitoring the new forwarding system. Documentation: https://mediamtx.org/docs/features/forward
Media-Over-QUIC
support publishing and reading through native QUIC (#6039)
fix inability to read some AV1 streams with RTSP (#6001) (#6006) Since v1.16.0, temporal unit delimiters were not stripped from AV1 streams anymore. This has been restored, healing AV1 streams read with RTSP.
make multicast errors on single interfaces non-fatal (bluenviron/gortsplib#1115) (#5574) When writing multicast packets to several interfaces at one, a write error to a single interface was fatal and prevented writing to the other ones. Fix this.
client: change mapping between URL and tcURL, app, streamKey (bluenviron/gortmplib#94) (#4676) URLs passed to clients are now mapped into RTMP-native fields (tcURL, app, streamKey) in this way: tcURL contains URL without credentials and without fragment, app contains path and query of tcURL, streamKey contains the fragment.
muxer: recompute PTS of MPEG-TS AAC (bluenviron/gohlslib#379) iOS requires a precise timestamp that is often not available in AAC streams. Recompute timestamp from scratch.
WebRTC
sort tracks in a deterministic way (#5988) (#5989) When ingesting tracks with WebRTC, track order was randomized, preventing multi-track always-available streams from working reliably, since they require tracks to be ordered in a precise way. WebRTC tracks are not ordered by MID, RID, trackID and streamID respectively.
reset recomputed audio PTS if it drifts too much (#6021)
fix "packet lost" error when routing streams from WebRTC (#6034) Chrome sometimes sends empty packets, that are discarded by the server, but the sequence number of following packets is not recomputed, leading downstream packet loss detectors to emit errors. This is fixed.
SRT
improve log clarity (#5990) use message 'passphrase not provided by client' when clients do not provide passphrases.
close sources immediately when path is closed (#6038)
Dependencies
code.cloudfoundry.org/bytefmt updated from v0.82.0 to v0.83.0
github.com/MicahParks/jwkset updated from v0.11.0 to v0.11.3
github.com/MicahParks/keyfunc/v3 updated from v3.8.0 to v3.8.1
github.com/bluenviron/gohlslib/v2 updated from v2.4.1 to v2.4.2
github.com/bluenviron/gortmplib updated from v0.4.1 to v1.0.0
github.com/bluenviron/gortsplib/v5 updated from v5.6.2 to v5.6.3
github.com/go-git/go-billy/v5 updated from v5.9.0 to v5.9.1
github.com/go-git/go-git/v5 updated from v5.19.1 to v5.19.2
github.com/pion/ice/v4 updated from v4.3.0 to v4.4.0
github.com/pion/interceptor updated from v0.1.46 to v0.1.47
github.com/pion/webrtc/v4 updated from v4.2.17 to v4.2.18
github.com/quic-go/quic-go updated from v0.60.0 to v0.61.0
github.com/quic-go/webtransport-go updated from v0.11.1 to v0.12.0
github.com/pion/sctp updated from v1.11.0 to v1.11.1
golang.org/x/time updated from v0.14.0 to v0.15.0
Security
Binaries are compiled from source code by the Release workflow, which is a fully-visible process that prevents any change or external interference in produced artifacts.
Checksums of binaries are also published in a public blockchain by using GitHub Attestations, and they can be verified by running:
ls mediamtx_* | xargs -L1 gh attestation verify --repo bluenviron/mediamtx
You can verify checksums of binaries by downloading checksums.sha256 and running:
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
MTA: Allow System Sieve scripts to access orcpt during the DATA stage.
Changed
S3: accessKey can now be read from an environment variable or file.
Fixed
Meilisearch: Verify index existence using GET instead of creating a new task which times out on busy servers.
Branding: Stalwart logo flashes before the per-tenant logo is loaded on the login page.
Calendar: iMIP and alarm notification messages embed the default logo using bare LF line endings, producing a single 4247 octet line that strict SMTP relays reject with line too long.
DMARC: Failure reports state Identity-Alignment: none when a mechanism authenticated successfully but against an identity that is not aligned with the From domain.
Redis: Task and queue locks are never released after a worker dies, because failed lock attempts refresh the lock expiry.
Recovery mode: Download WebUI if missing.
Logging: The systemd journal tracer omits the parent span's fields.
MTA:
BDAT chunks sent without a valid MAIL FROM are answered with 552 5.3.4 Message too big for system instead of 503 5.5.1.
A maxMessageSize of 0 rejects every message with 552 5.3.4 Message too big for system instead of disabling the size limit.
Windows: Listeners bound to the unspecified IPv6 address ([::]), including all defaults, refuse IPv4 connections such as 127.0.0.1, since IPV6_V6ONLY is enabled by default on Windows.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
Changed
Fixed
JMAP:
Email/copy should return alreadyExists when copying a message to a mailbox that already contains it.
Email/copy with onSuccessDestroyOriginal destroys the copy's creation id instead of the source Email id.
CalendarEvent/set does not generate a uid on create when the client omits it.
CalendarEvent/set does not refresh the updated property (iCalendar DTSTAMP) on create or update.
EmailSubmission/set rejects valid recipients whose domain is itself a public suffix (e.g. gov.in, co.uk).
Requests are rejected with notRequest when a method name contains a JSON-escaped solidus (e.g. Core\/echo).
MTA: Panic when MTA-STS is disabled and a remote MTA fetched /.well-known/mta-sts.txt.
Auth: Scoped credentials with SysApiKeyCreate or SysApiKeyUpdate permissions can regain its own account's full rights.
Web Push: Valid VAPID keys are rejected when PEM-encoded with explicit EC parameters, in SEC1 (EC PRIVATE KEY) format, or with a leading byte-order mark.
Encryption at rest: Appended messages are encrypted for accounts that did not opt in to encryptOnAppend.
Cache: Account caches silently discard entries larger than a single quick-cache shard, causing constant database rebuilds.
Registry: Id references (e.g. #certificate-...) fail to resolve on defaultCertificateId, defaultAdminRoleIds, listenerIds and publicKey.
Search: reindex drops calendar and contact index tasks for accounts with fewer than a full batch of items.
Migration: Abort --import when the target already contains data in the key range being imported.
Cluster: Broadcast subscriber re-subscribes after every message, losing bursts of cluster broadcasts during the reconnect window.
Enterprise: Per-tenant logo is not shown on the OAuth login password and OTP screens, which are served from the server's canonical host rather than the tenant domain.
fix error message (#5922) 'all' is a synonym for 'all_others'
fix clearing lists with environment variables (#5410) (#5924) MTX_AUTHINTERNALUSERS_0_IPS, MTX_LOGDESTINATIONS and MTX_RTSPTRANSPORTS can now be used to clear their corresponding list by setting them to an empty value.
add runOnOnline / runOnOffline hooks (#5399) (#5956) These are triggered and a stream is online (i.e. not just provided by an offline segment).
rename runOnReady into runOnAvailable, runOnNotReady into runOnUnavailable (#5957)
fix wrong PTS and wrong playback of alwaysAvailableFile (#5436) (#5960) PTS offset of samples was not properly considered, and sleep between samples was PTS-based instead of being DTS-based.
avoid potential timing attack when validating SHA256 credentials (#5961) The == operator is vulnerable to timing attacks as it short-circuits on a mismatch. Use ConstantTimeCompare to avoid this vector. Co-authored-by: Tristan Matthews tmatth@videolan.org
normalize authentication error messages (#5421) (#5959) Log authentication errors as soon as possible, use the "warn" level, use the same message whatever the author or protocol.
adjust code to prevent security scan false positives (#5963) about string escaping.
ask for credentials only in case of protocols that support it (#5966) When clients connect with some protocols (SRT, RTMP), they are unable to provide credentials even if they are asked to. In this case, it's useless to wait for credentials, and it's better to immediately log authentication errors and apply the anti-brute force algorithm.
generate most of OpenAPI automatically (#5918) enums and structs are now generated automatically. This eliminates some inconsistencies and makes development easier.
Media-Over-QUIC
fix race condition during startup (#5965) allocate the HTTP server only after the MoQ server has been initialized.
fix several panics and OOM errors (#5964) Check for limits before allocating memory by using sizes passed from the remote peer. Also add fuzzing to all MoQ primitives.
support draft-19 of the specification (#5968) * support draft-19 of the specification * support subscribing the same track multiple times.
prevent excessive CPU consumption in reorderer (#5976) do not iterate by maxGroupID (passed by user) but iterate by internal pending packets (uncontrolled by user).
WebRTC
prevent cross-origin unauthorized access (#5975) when a user had previously inserted credentials into a MediaMTX instance through a browser, and AllowOrigins was set to a wildcard, third-party websites visited by the user were allowed to read streams without restrictions. This is now prevented by returning "*" in Access-Control-Allow-Origins when AllowOrigins is a wildcard, a behavior that prevents browsers from sharing credentials with third-party websites.
RTSP
restrict UDP port range to 32768-60999 (#5398) (#5958) this is the default Linux ephemeral port range.
use session ID in requests to the external authentication server (#5977) Co-authored-by: Cycle1337 Cycle1337@outlook.com
prevent cross-origin unauthorized access (#5975) when a user had previously inserted credentials into a MediaMTX instance through a browser, and AllowOrigins was set to a wildcard, third-party websites visited by the user were allowed to read streams without restrictions. This is now prevented by returning "*" in Access-Control-Allow-Origins when AllowOrigins is a wildcard, a behavior that prevents browsers from sharing credentials with third-party websites.
SRT
fix compatibility with StreamToStudio app (#5414) (#5928)
Dependencies
code.cloudfoundry.org/bytefmt updated from v0.78.0 to v0.82.0
github.com/alecthomas/kong updated from v1.15.0 to v1.16.0
github.com/bluenviron/gohlslib/v2 updated from v2.4.0 to v2.4.1
github.com/bluenviron/gortmplib updated from v0.4.0 to v0.4.1
github.com/bluenviron/gortsplib/v5 updated from v5.6.1 to v5.6.2
github.com/bluenviron/mediacommon/v2 updated from v2.9.1 to v2.9.2
github.com/matthewhartstonge/argon2 updated from v1.5.5 to v1.5.6
github.com/pion/ice/v4 updated from v4.2.8-0.20260604162030-72f5001c4596 to v4.3.0
github.com/pion/interceptor updated from v0.1.45 to v0.1.46
github.com/pion/rtcp updated from v1.2.16 to v1.2.17
github.com/pion/rtp updated from v1.10.2 to v1.10.5
github.com/pion/webrtc/v4 updated from v4.2.15 to v4.2.17
github.com/pires/go-proxyproto updated from v0.12.0 to v0.15.0
github.com/quic-go/webtransport-go updated from v0.11.0 to v0.11.1
golang.org/x/crypto updated from v0.53.0 to v0.54.0
golang.org/x/net updated from v0.56.0 to v0.57.0
golang.org/x/sync updated from v0.21.0 to v0.22.0
golang.org/x/sys updated from v0.46.0 to v0.47.0
golang.org/x/term updated from v0.44.0 to v0.45.0
github.com/pion/datachannel updated from v1.6.0 to v1.6.2
github.com/pion/dtls/v3 updated from v3.1.4 to v3.1.5
github.com/pion/sctp updated from v1.10.0 to v1.11.0
github.com/pion/stun/v3 updated from v3.1.5 to v3.1.6
github.com/pion/turn/v5 updated from v5.0.9 to v5.0.12
golang.org/x/text updated from v0.38.0 to v0.40.0
Security
Binaries are compiled from source code by the Release workflow, which is a fully-visible process that prevents any change or external interference in produced artifacts.
Checksums of binaries are also published in a public blockchain by using GitHub Attestations, and they can be verified by running:
ls mediamtx_* | xargs -L1 gh attestation verify --repo bluenviron/mediamtx
You can verify checksums of binaries by downloading checksums.sha256 and running:
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
Use of Voluntary Application Server Identification (VAPID) in JMAP Web Push (RFC 9749).
Changed
Fixed
IMAP:
Mailbox object-quota only enforced in JMAP.
Pipelined STORE and EXPUNGE can execute out of order.
JMAP:
Read-only sharee cannot set isSubscribed on a shared mailbox.
Web Push payloads with Content-Encoding: aes128gcm should not be base64-encoded but sent as raw bytes.
Stale push subscription can block verification of a new one.
PushSubscription/set rejects the unpadded base64url keys the W3C Push API produces.
Email/import does not send push notifications for imported messages.
CalendarEvent/set silently ignores ifInState.
CalDAV: calendar-query REPORT returns empty calendar-data for JMAP-created events.
MTA:
DMARC is skipped when MAIL FROM SPF is unavailable.
queue_name variable not available in rate limiter expressions.
Calendar:
No expanded occurrences are returned for a daily recurrences crossing DST.
Uppercase MAILTO calendar addresses become invalid SMTP recipients.
Scheduling invitations on a shared, non-owned calendar fail with MAIL FROM unauthorized.
HTTP: Disable allowedEndpoints expression in recovery mode.
Telemetry: Tasks are serialized to the wrong store when using separate stores for telemetry and data.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
FreeBSD support.
Changed
Fixed
OAuth resource indicators: Accept imap, smtp, pop3 and sieve as valid resource indicators for OAuth access tokens.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
fix race condition that prevents decoding the stream (bluenviron/mediamtx-rpicamera#109) (#5861) When a player immediately connects to a newly-created stream, SPS/PPS might not be available, neither in the SDP and neither in-band. Prevent the issue by always sending SPS/PPS in-band.
code.cloudfoundry.org/bytefmt updated from v0.76.0 to v0.78.0
github.com/abema/go-mp4 updated from v1.6.0 to v1.7.1
github.com/bluenviron/gortsplib/v5 updated from v5.6.0 to v5.6.1
github.com/bluenviron/mediacommon/v2 updated from v2.9.0 to v2.9.1
github.com/matthewhartstonge/argon2 updated from v1.5.4 to v1.5.5
github.com/pion/sdp/v3 updated from v3.0.18 to v3.0.19
github.com/quic-go/webtransport-go updated from v0.10.0 to v0.11.0
github.com/pion/srtp/v3 updated from v3.0.11 to v3.0.12
github.com/bluenviron/mediamtx-rpicamera updated from v2.6.0 to v2.8.0
Security
Binaries are compiled from source code by the Release workflow, which is a fully-visible process that prevents any change or external interference in produced artifacts.
Checksums of binaries are also published in a public blockchain by using GitHub Attestations, and they can be verified by running:
ls mediamtx_* | xargs -L1 gh attestation verify --repo bluenviron/mediamtx
You can verify checksums of binaries by downloading checksums.sha256 and running:
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
Encryption-at-rest: Support for AES-256-GCM and ChaCha20-Poly1305 for S/MIME (#161).
S3: Support for allowInvalidCerts option to allow connecting to S3 endpoints with invalid TLS certificates.
Redis Sentinel support as an in-memory store and cluster coordinator backend (#2430).
Changed
Fixed
DANE: Verify DNSSEC is supported by the resolver before attempting to validate TLSA records.
TLS: Update search index when file-backed certificates are refreshed.
JMAP: Principal/query returns broad results when a name or email filter cannot be resolved.
Webhooks: event IDs collide for same event type emitted in the same second.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
If you are upgrading from v0.16.x, replace the binary (or run docker pull). If you are upgrading from v0.15.x and below, please read the upgrading documentation for more information on how to upgrade from previous versions.
Added
ACME: Allow specifying a preferred certificate chain.
Changed
Fixed
JMAP: */changes methods leak ids of non-shared objects (reported by @5ud0er).
Sieve: Do not allow invalid certs in http_header function.
FoundationDB: Fix read version cache expiration logic.
MTA: Re-scheduling or editing a queued message reports success but persists nothing for recipients in a non-default virtual queue.
CardDAV: Version requests included in address-data are ignored.
ACME: Add freshness check when renewing certificates.
Autodiscover v2: Read email address from query parameters.
Sieve: Do not keep copies of redirected messages when keep is not specified.