Remove launch DNS wait and paint saved people immediately #50
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fm/tv-speed-dns"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
AI-assisted (fork-internal): implementation and PR description.
Change
PersistentDnsthrough Jellyfin SDK, Conductarr, artwork/playback and crash reporting. System IPv6/IPv4 ordering is preserved. Successful answers live in memory and a privatenoBackupFilesDir/wolfskin-dns.jsonfile, capped at 32 persisted hosts. Answers return immediately up to 24 hours old; background revalidation is due after 60 seconds. Failed background lookups back off for 2 seconds without extending the successful timestamp. Negative answers are not cached or persisted; missing, oversized and corrupt files fall back to the system resolver. No new dependency or Android DNS-setting change.Users/MeandSystem/Info/Publicconcurrently during session validation rather than serializing them.docker/README.md.Review correction and current verification
Current pushed head:
052d0b8898c40abd6b357ac5f7802a31d53d4b29. Worktree clean.Fixed the WebSocket launch-crash review: OkHttp's listener-free derived WebSocket client bypasses HTTP retry bookkeeping rather than throwing for a missing call entry (B1). Also use a name-column-only SQL update and reload the latest saved user so concurrent PIN/preferences/home-settings edits survive revalidation (N2), keep DNS hints in
noBackupFilesDir(N5), and contain unexpected resolver exceptions at the refresh/warm-up boundaries (N6).newBuilder()-derived configured client, receivedready, and observed no uncaught dispatcher exceptionmake apk-debugAll 22 requested review regressions passed. No full
make testwas run for this correction, per the review gate; the prior full-suite failures below remain tracked, not hidden or claimed fixed. No native SIGABRT, box action, device connection, ADB, installation or optimized build.Deferred follow-ups, not changed in this correction: revoked-token routing (N1), interim null user-DTO UI (N3), uncapped in-memory DNS hosts (N4), stricter numeric-literal validation (N7), stale-address exposure for cleartext HTTP origins (N8), and bookkeeping for unexecuted/unclosed calls (N9). Intent/deep-link restoration remains the existing blocking validated path.
Original implementation verification
Original tested head:
327f631f0de63312c7c1e165dc3dae02b9db6cc1. The following is historical evidence, not a new full-suite run on the correction.127.0.0.2refused; fresh resolution to127.0.0.1recovered the profile with one HTTP request and two resolutions. Reopened disk cache, ETag 304 and HTTP 503 saved-data retention also passedmake testmake apk-debugThe actual Compose picker was captured and visually inspected while the first household HTTP response was blocked: Justin, Mindel and Shared all displayed with the normal colors and selected-person ring. Proof files are under
/root/.treehouse/firstmate-7bab20/2/firstmate/data/tv-speed-dns/. No native SIGABRT occurred; no serial rerun was needed. No ADB, device connection, installation, optimized APK build, live endpoint mutation or Private DNS change.Exact full-suite failures
All are
PersonPickerSessionTesttimeouts. Comparison is with the prior episode-navigation and Resume review's six-test list.launchRightThenOneOkKeepsPickerClosedfastLeftLeftOkKeepsPickerClosedlaunchLeftThenOneOkKeepsPickerClosedavatarLeftThenOneOkKeepsPickerClosedThe reduced failure count is not a claim that this change fixes the suite-order issue. Person switching still requires the device review below.
On-device review — owner installation only
GET https://jelly.justinvisser.org/Users/MeandGET https://jelly.justinvisser.org/System/Info/Public; ignore later foregroundUsers/Merefreshes. The spike's slow gaps were 1.508 s and 1.509 s. These requests now overlap and either may log first; compare their timestamp separation and verify the picker does not wait for either response. The minified log tag can change between builds.Boundaries
The local picker is saved identity metadata, not proof of current server authorization. Revoked credentials remain subject to normal server rejection. A dead IP can still incur the configured connection timeout before fresh-DNS recovery; cached DNS does not bypass TLS validation. Debug APKs are not playback-complete, and the optimized combined build/device timing remain with firstmate and the owner.
Review (review/security-claude): BLOCK: every Jellyfin WebSocket connection crashes the app
Reviewed SHA:
327f631f0de63312c7c1e165dc3dae02b9db6cc1Blocking finding
B1. The DNS retry interceptor throws on WebSocket calls.
DnsConnectionRetry.interceptstarts withattempts.getValue(chain.call())(DnsConnectionRetry.kt:37). Entries are only created in itsEventListener.Factory.create. OkHttp 5.5.1 opens WebSockets onclient.newBuilder().eventListener(EventListener.NONE)(RealWebSocket.kt:153-158). That copy keeps the application interceptor but never calls our factory, so the lookup throwsNoSuchElementException.AsyncCall.runthen rethrows it on theOkHttp Dispatcherthread (RealCall.kt:592-602), and on Android that kills the process.The Jellyfin SDK 1.7.1 socket goes through exactly this path:
OkHttpFactory.createClient = base.newBuilder(), thenOkHttpSocketConnection: client.newWebSocket(...). The base is the shared@StandardOkHttpClient(AppModule.kt:97-98,:136-140,:155).ServerEventListenersubscribes as soon ascurrentis non-null (ServerEventListener.kt:52-90), so the crash follows every signed-in cold launch, person switch and first sign-in. Playback (PlaybackViewModel.kt:1468) and music (MusicService.kt:437) subscribe too.Reproduced on a JVM with the real
okhttp-jvm-5.5.1.jarand a line-for-line port of the interceptor. A plain HTTP call through the same client works, so the HTTP-only tests can't catch this:Fix:
val attempt = attempts[chain.call()] ?: return chain.proceed(chain.request()), plus a regression test: a WebSocket through aconfigured client derived withnewBuilder(), againstMockWebServerwithwebSocketUpgrade.Contract checks that hold
ServerOnlybefore the token is loaded (ServerRepository.kt:128), and the hot-reload and picker guards are unchanged.UserDto.idcheck andcompareAndSetpublish. A failed refresh only logs; saved people are never deleted, and no token is persisted or logged.retryOnConnectionFailure(false). TLS still verifies the URL host, so a cached IP can't bypass hostname checks.Users/MeandSystem/Info/Publicnow run in parallel inside a structuredcoroutineScope. AUsers/Mefailure cancels the other call, and cancellation is now rethrown.Non-blocking notes (full detail in the report)
WolfskinApp.kt:217). Suggest routing a 401 or 403 toUserListwhile_current === expected.revalidateSessionwrites back the whole user row from the restore-time snapshot (ServerRepository.kt:141; full-row@Update). It can revertappPreferencesandhomeSettingsSourcechanges made during the refresh. In a millisecond window between:139and:141it could also revert a newly set PIN. Update only the name column.currentUserDtois null until revalidation orappResumefills it, so the nav drawer is empty and default home rows assume no live TV in that window.filesDir, which Android backs up (allowBackup="true"). PrefernoBackupFilesDir.NetworkStartupcatch onlyUnknownHostException; anything else would be uncaught.:.http://origins only, a 24 h stale IP can send the auth header in cleartext to whatever LAN device now holds that address.attemptsmap leaks entries for calls that are never executed or never closed.What I read
The full
git diff origin/main...HEAD(18 files), plusServerRepository,MainActivitystart/resume,WolfskinApppicker selection,JellyfinServerDao,NavDrawerService,HomeSettingsService,ServerEventListener,AppModule,ConductarrClient,CrashReportSenderFactory,WholphinApplicationand the manifest. Upstream sources at the pinned versions: OkHttpparent-5.5.1RealWebSocket/RealCall, jellyfin-sdk-kotlinv1.7.1OkHttpFactory/OkHttpSocketConnection. No Gradle or make was run.Delta review of the fix commit
052d0b8898(read in full: 10 files, +144/-17) against the Opus security review's blocking finding on327f631f.B1 (WebSocket crash) is fixed:
DnsConnectionRetry.interceptnow usesattempts[chain.call()] ?: return chain.proceed(chain.request()), so OkHttp's derived WebSocket client (EventListener.NONE) passes through with no retry. A regression test opens a WebSocket through aconfigure()d client derived withnewBuilder()against MockWebServer.The cheap non-blocking notes were folded in as asked: N2 revalidation now updates only the
namecolumn (newupdateUserNameDAO query) and re-reads the row, so it can no longer revert preferences or a PIN change made while the refresh was in flight; N5 the DNS file lives undernoBackupFilesDir; N6 the refresh task andNetworkStartup.warmcatchException, not onlyUnknownHostException. N1, N3, N4 and N7 to N9 stay as follow-ups.No new blocking finding. Checks: lane ran focused WebSocket/DNS, real-Room saved-session, saved-picker and seven person-switch tests and one
make apk-debugon this head; no full suite (known unrelated picker timeouts, separate fix in flight). Forgejo Actions is disabled on this repo.