CI Report:
https://ci-tests.linuxserver.io/linuxserver/tautulli/develop-1c0a44ae-ls487/index.html
LinuxServer Changes:
No changes
Remote Changes:
Tautulli Remote Relay Support (#2772)
- Deliver Tautulli Remote App notifications through the push relay
Devices now register a push token alongside the OneSignal ID, and each
notification picks its transport from the device it is addressed to: a
device with a push token goes to the relay, everything else keeps taking
the OneSignal path unchanged. One server can hold a mix, so an updated
phone and a tablet still on the old app both keep working.
The relay caps a notification at what FCM will carry, so an over-long body
is shortened rather than dropped, and a dead token reported by the relay
clears the device's official flag instead of being retried forever. A
successful delivery re-derives that flag too, so a device whose validation
was refused or interrupted recovers without being registered again.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
- Refer to the relay as the Tautulli Remote relay
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
-
Name the relay host in the device registration note.
-
Tidy the relay notifier's logging.
The response body was logged raw while every other request in this file
passes it through server_message first. The relay URL is a config value,
so an unexpected host could put its whole error page, newlines included,
into the log.
A dead push token was reported at error level on every send. Clearing the
official flag only drops the device from the notifier config list, not
from an agent already saved against it, so a phone the user stopped using
would repeat one identical error indefinitely. It now reports once and
falls to debug after.
-
Create a Notification Device ID to match Tautulli Remote and use that instead of exposing the token used with the relay.
-
Update the notification logging to specify the device and correct the rate limit time.
-
Bring the relay code in line with the rest of the file.
An editor save had prepended a UTF-8 BOM to notifiers.py, making the
first hunk of the diff a no-op edit to a line the branch never touched.
The two ellipsis characters were the only non-ASCII in an otherwise
pure ASCII file, where the existing truncations all use three dots.
The new code carried several times the comment density of its
surroundings, including multi-line reasoning blocks and docstrings in a
module that has none. What is left is the handful of facts the code does
not show on its own: why the base64 budget inverts rather than scales,
why newlines are stripped from a device name, and why clearing the
official flag does not stop an already configured agent from sending.
_trim_to_relay_limit and _send_via_relay move above agent_notify, where
every other notifier in this file keeps its private helpers, and their
two constants move onto the class alongside NAME rather than sitting
between the two module level registries. Log calls no longer lead a
continuation line with .format(, and the conditional SQL splices with
%s as it does elsewhere in the same file.
No behaviour change.
- Revalidate mobile devices on every startup
The reworked revalidation pass was left on its original call site, the
one-shot UPGRADE_FLAG gate in upgrade(). That flag has shipped set since
v2.10.0, so on an existing install the pass never ran and a device left
at official = -1 by an outage was never repaired.
Call it from initialize() instead. It already runs off the startup thread,
skips validated devices, and paces itself for the relay's per-IP limit.
Devices holding the disabled sentinel are skipped too, since they have no
registration to validate.
UPGRADE_FLAG now has no readers; its config key is left in place.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
- Refuse a relay notification that cannot be trimmed to fit
_trim_to_relay_limit only shortened the body, so a subject large enough to
exceed the budget on its own left the body replaced by an ellipsis and
logged that it had been shortened "to fit" when it had not. With the
cryptography library present the relay then refused the oversized envelope
with a 413; without it the plaintext escaped the cap and the notification
was delivered with its body destroyed and nothing logged as an error.
Shorten the subject as well when emptying the body is not enough. The body
still goes first, since the subject is the headline. An empty field is left
alone rather than reported as shortened from zero to zero.
Compute the budget for the envelope actually being sent. It was derived
from the base64 ciphertext and applied to both paths, so the unencrypted
payload, which is embedded as is, was trimmed about a kilobyte earlier than
it needed to be.
The size is re-checked after trimming and the notification is dropped if it
still does not fit. Nothing should reach that branch now that the subject is
trimmable; it is there so a future change to the limit cannot quietly
restore the old behaviour of sending something that does not fit.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
- Stop the relay requests following redirects
Neither relay call set allow_redirects, so requests' default applied. A 307
or 308 re-sends the method and body to the Location target, and the push
token travels in the JSON body rather than a header, so requests has
nothing to strip. The relay only ever answers 200, 410, 413, 429 or 5xx, so
a redirect is always anomalous, and the destination is now operator
settable.
Both call sites already handle the refusal: validation treats anything but
200 or 410 as indeterminate and leaves a validated device alone, and the
notifier logs the status code as a failure.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
- Stop the relay note claiming an unencrypted notification is encrypted
The note is appended to whichever help block precedes it. With the
Cryptography library missing that is the warning that notification content
will be sent unencrypted, which the note then contradicted by saying the
relay forwards the encrypted notification. In that configuration the
notification really is sent as plain text.
The OneSignal note this replaced held either way, since it only said that
some user data cannot be encrypted. Drop the word so the note is again true
in both branches; whether the content is encrypted is already stated by the
block above it.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
- Restore the OneSignal notice for a device still delivered through it
The help text said notifications are delivered through the Tautulli Remote
relay and Firebase, but a device registered by an older app version is still
delivered through OneSignal. The OneSignal data collection notice was removed
with the rest of the OneSignal wording even though that path stays in use
until the app versions using it are gone.
Append the notice again when the agent's own device would be delivered
through OneSignal. The device is resolved the way agent_notify resolves it,
by device_id, rather than from get_devices(), whose official filter would
hide the notice for a device demoted by a failed validation while the agent
kept delivering to it.
The transport decision moves into _via_relay() and is shared with
agent_notify, so the notice and the send path cannot disagree. A device that
opted out holds the disabled sentinel, so testing the token for emptiness
would pick the wrong transport for it.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
- Always verify the relay certificate
The notify call went through request_response2, which applied
VERIFY_SSL_CERT to it, so a user who turned that off for a self signed Plex
server also stopped verifying the relay. The validate call did not, because
it uses requests directly and never passed verify. The two relay calls
disagreed for no reason other than which helper they happened to use.
VERIFY_SSL_CERT exists for the user's own server. The relay is a third party
host and the push token travels in the request body, so verification should
not follow that setting. Pin it on both calls.
request_response2 assigned kwargs['verify'] rather than defaulting it, so a
caller could not require verification for one request; the value was
silently discarded. Use setdefault. No other call site passes verify, so
nothing else changes.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
- Tidy the relay helpers
validate_push_token's None branch cannot be reached. Its only caller routes
a falsy token to validate_onesignal_id instead, where the equivalent branch
is live.
revalidate_onesignal_ids validates relay push tokens as well as OneSignal
IDs, so name it for what it does. It is the wrapper over _revalidate_devices.
_send_via_relay's headers argument was always the same constant, and
requests sets Content-Type itself when json= is used, so the relay request
sends identical bytes without it. The OneSignal payload still needs the
header, so the assignment moves down to it.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
- Name the device in every relay failure log
The six failure branches used three phrasings and named the device in two of
them. Splitting the rate limit branch in two dropped the device name from the
burst path while the daily cap path kept it, so the branch that fires on an
ordinary burst logged only a token hash, which nothing can resolve to a phone.
Build the label once and use one phrasing everywhere. A device with no
friendly or device name still logs just the id.
Co-Authored-By: Claude Opus 5 noreply@anthropic.com
- Address review feedback
Move revalidate_devices into start() so it runs once Tautulli is up, drop
UPGRADE_FLAG now that nothing reads it, and let requests set Content-Type
from json=.
The rate limit log said to retry after N seconds, but nothing retries and
the notification is dropped. The response body was logged after every
failure branch had already said what went wrong.
- Drop the redundant verify argument
requests already defaults to verifying, and this call does not go through
request_response2, so VERIFY_SSL_CERT was never in play here.
-
Build the validate URL with an f-string
-
Build the relay URL and trim message with f-strings
-
Correct the relay note in the registration modal
-
Refuse to send a notification for an unknown platform
The relay builds a different message per platform, so a device with no
recorded platform was sent as android — which an iOS device receives but
never decrypts, because its extension is not triggered.
set_official no longer writes the guess to the database either.
Co-authored-by: Claude Fable 5 noreply@anthropic.com