diff --git a/docs/p2p-transfer.md b/docs/p2p-transfer.md index c3c1d8c..d40c6ef 100644 --- a/docs/p2p-transfer.md +++ b/docs/p2p-transfer.md @@ -63,7 +63,91 @@ into OPFS through WebRTC and assert that server HTTP/WS logs contain only signalling and metadata. Exercise interruption/resume and explicit fallback. Review real iPhone foreground/background behaviour and restrictive NAT failure. -## Implementation status +## Implemented behaviour -This document records the pre-implementation audit and design. Transport, -authorization and UI changes described above are not yet implemented. +- `direct-protocol.js` strips unexpected fields and validates file claims and + bounded signalling; `direct-relay.js` binds invitations to authenticated room + members. Accepting an invitation is one-use, and only the bound pair can signal. +- Remote, presenter and party relays carry the new protocol. Presenter and OBS + still display lyrics/control metadata; they do not automatically play media. + A presenter can explicitly accept a file sent by its paired host. +- Settings → Direct device transfer provides the default-on toggle and send + buttons for currently connected paired/party/profile peers. Send the currently + playing **saved** song; the receiving device confirms every copy. +- The paired remote has “Play here from paired screen” when its host has saved + media. Party guests and ordinary playback resolve an advertised direct source + before loading media. Saves and exports do the same; the existing OS export + sheet is preserved. +- `/ws/p2p` now has a profile-scoped, ephemeral file-id directory. Protected + profiles require password/key proof, checked against the server profile hash. + Unprotected profiles retain their existing name-based access policy. Claims + are **not** admitted as server-verified media. Each device advertises at most + 1,000 local ids per directory update; use paired-room sending for larger libraries. +- The legacy unconfirmed global signalling path is refused. Older clients need + to reload the updated application to transfer files. This closes a bypass of + receiver confirmation and room/profile membership. +- `direct-media.js` uses public STUN, ordered 64 KiB DataChannel chunks and + bufferedAmount backpressure. `direct-recv-worker.js` writes partials directly + to OPFS, rehashes their retained prefix and verifies exact size plus SHA-256 + before promotion. File extensions are preserved. A disconnected partial can + be resumed by sending/requesting the same file again, including non-aligned offsets. +- The 10-second deadline covers connection establishment. Prefix rehashing and + final verification do not consume it. Idle transfer timeout is 30 seconds. + Failures retain partial data and offer an explicit **Use server copy** action. + Automatic holder-to-server rehydration is blocked while direct mode is on; + **Verify & share** remains an intentional, explicit server upload. +- `direct-stream.js` supports progressive preview for compatible fragmented + H.264 MP4 through MediaSource. Ordinary faststart MP4, unsupported codecs, + unavailable MediaSource and preview buffer exhaustion use the complete file. + iPhone playback should be reviewed using that completed-file path. A preview + is interactive and independent of the main player's host clock; main party + playback follows the host after the verified copy is ready. + +## Paths that still use server media + +Original YouTube acquisition, server uploads, intentional Verify & share intake, +external VLC/M3U HTTP URLs and explicitly accepted server fallbacks still use the +server. If no connected device advertises a local copy, normal original-source +playback/download is preserved; this is not a transfer of another device's file. +Profile/playlist shares, chapters, lyrics, presenter state and OBS state remain +metadata/control only. Direct discovery requires an online paired room or an +existing profile, and the browser must remain available to serve its OPFS file. +No TURN service, deployment change or secret configuration is required. + +## Checks and reviewer follow-up + +Run: + +```sh +node --test frontend/*.test.js +node --check frontend/app.js +bun build --no-bundle server/server.js +bun test server/direct-relay.test.js server/p2p-hub.test.js server/remote.test.js server/party.test.js +npx playwright test -c playwright.direct.config.js +npx playwright test -c playwright.glass-navigation.config.js +``` + +The two-page localhost test uses the production relay and transport, separate +browser stores, a real RTCDataChannel and actual OPFS writes. It resumes a 2 MB +file at byte 12,345, verifies the resulting SHA-256 and asserts that the server +log contains only invitation/acceptance/signalling/completion. A second case +adds 12 seconds of resume preparation to prove it is outside the 10-second +connection deadline. This is a local transport proof, not proof that arbitrary +NATs will connect. + +Check real iPhone foreground/background suspension, large-file quota and final +promotion, iOS completed-file playback, restrictive/symmetric NAT fallback, +paired screen playback, party host changes during a copy, protected-profile +credential changes/reconnect, OS export gestures, and an actual fragmented MP4 +MediaSource preview on desktop. Public STUN cannot overcome every NAT; declined +invitations do not start media acquisition. Peer directories update when the +presence socket connects and when local saves complete, rather than promising +availability of offline/backgrounded devices. + +Final verification on 2026-10-03: 123 frontend unit tests, 22 touched server + tests (including remote and party), app syntax and server build, 8 Glass/Classic +navigation browser tests at 390/1440 px, and both direct-transfer browser cases +passed. The first navigation run lost its reused localhost server; a fresh-server +rerun passed all eight cases without a code change. Classic screenshot comparisons +remain within the test's rasterization tolerance. No real iPhone or restrictive +NAT test was performed here, and no code was pushed or deployed.