The admin Activity page could not name the build a session was streaming
from. Android already sent X-Silo-Client-Version and the server already
stored it intact, but playbackClientDisplayName routed it through
shortPlaybackClientVersion, which strips non-numeric runes, truncates to
two components, and drops a trailing ".0" — so a client reporting "1.0.0"
rendered as "Silo Android TV 1". The Apple clients sent no client name or
version at all and fell back to user-agent sniffing.
Adds two additive, opaque wire fields alongside the existing client
headers — X-Silo-Client-Build (<=64) and X-Silo-Client-Channel (<=32) —
with client_playback_context.app_build/app_channel as the v3 fallback,
which is also where the previously discarded app_version now gets used.
The server never parses, compares, or enum-validates either value: Apple
uses a per-platform TestFlight sequence and Android a per-marketing-
version counter, and keeping them opaque lets both coexist without a
shared scheme. Any future minimum-version gating belongs on
client_version, which is semver.
Only the named-client branch of playbackClientDisplayName stops
truncating; the user-agent branch keeps shortPlaybackClientVersion, so
browser labels stay "Chrome 120" rather than a full UA version string.
The compact session row is unchanged in width — it is shared with
AdminDashboard, AdminStats, and HouseholdStreamsPanel — and the exact
string lands in the row tooltip and a new Client card in the expanded
panel.
Diagnostic logs carry client_name/version/build/channel on both
"playback plan decided" lines and on session expiry. opslog stores an
open attrs JSONB, so this needs no migration. activity_log is
deliberately untouched: it is the highest-volume table and the value is
constant per device.
Jellyfin compat sessions keep an empty build — the MediaBrowser auth
header vocabulary has no build concept, and synthesizing one from a user
agent would be a guess.
Part of the client-version-visibility work spanning silo-android and
silo-apple.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>