Files
archy/.planning/quick/260729-hj1-peer-files-media-batch-wavlake-paid-trac/260729-hj1-SUMMARY.md
T

9.5 KiB

phase, plan, subsystem, status, requirements, key-files, commits, metrics
phase plan subsystem status requirements key-files commits metrics
quick-260729-hj1 01 demo / peer-files complete
A1
A2
A3
A4
A5
A6
A7
A8
created modified
demo/content/music/Zazawowow - WEBFIVEFOURTHREETWOONE.mp3 (6,041,903 B, MPEG audio)
demo/content/music/Zazawowow - Michael Michael Saylor.mp3 (4,517,590 B, MPEG audio)
demo/peer-media/artwork-webfive.jpg (1400x1400 JPEG)
demo/peer-media/artwork-michael-saylor.jpg (1400x1400 JPEG)
neode-ui/mock-backend.js
neode-ui/vite.config.ts
neode-ui/src/views/PeerFiles.vue
neode-ui/src/views/__tests__/PeerFilesRefresh.test.ts
demo/peer-media/photo-*.jpg (all 10 replaced with real Wikimedia Commons photos)
14d1a453 feat(demo): Wavlake tracks, real photos, deduped peer catalog, working paid flow
f52c5407 fix(peer-files): free-image lightbox, click-to-open routing, in-app open after every payment rail
duration tasks completed
64m 3 2026-07-29

Quick Task 260729-hj1: Peer-Files Media Batch (Wavlake, Paid Track, Real Photos) Summary

Two real Wavlake tracks (paid buy->autoplay flow now delivers real mp3 bytes), seeded purchase history, deduped peer catalog, ten real Commons photographs, and a shared-frontend fix so every peer file click opens the right viewer (free images finally open in the lightbox).

What Was Done

Task 1 — Demo dataset (commit 14d1a453, demo/mock only)

  • Wavlake tracks (A1-A3): metadata fetched live from catalog.wavlake.com/v1/tracks/{id} (title/artist/album/duration confirmed against plan recon), mp3 bytes + 1400x1400 artwork downloaded from the CloudFront CDN and committed. song-webfive ("WEBFIVEFOURTHREETWOONE" by Zazawowow, 21 sats, PAID) and song-michael-saylor (free). Both verified as real MPEG audio with file(1).
  • Real paid bytes (A3): the shared content.owned-get / download-peer-paid / -invoice / -onchain handler now returns real disk bytes with the correct mime_type when the entry has them (text placeholder only for the fictional no-disk films/books). Ecash rail-deduction logic preserved verbatim.
  • Purchase history (A4): per-session sessionOwnedContent() store lazily seeds 3 dated purchases (song-builders 100 sats — audio with real bytes; film-the-signal 2100; book-cypherpunk-essays 210), computed against the session's own federation onions/catalog slices. Every purchase path appends, so Owned badges survive the post-purchase loadOwned() refresh. song-builders was converted from free to paid (100 sats) so the seeded history contains a playable paid audio item without pre-owning the showcase Wavlake track.
  • Dedupe (A5): peerCatalogFor is now a deterministic one-peer-per-item assignment (index % 12 against the session's node order) plus a 3-item POPULAR list (song-webfive, film-block-height, photo-aurora-fjord) that appears on exactly one extra peer each. Verified: 29/29 ids present, exactly 3 duplicated ids, both Wavlake tracks on trusted peers.
  • Session-stable onions: demoFederationNodes() was regenerated (fresh random onions) on every RPC call; it is now memoised per session (sessionFederationNodes()) so catalogs, owned records and the Federation view agree.
  • Real photos (A6): all ten photo-*.jpg picsum placeholders replaced with real Wikimedia Commons photographs (aurora over Lofoten fjord, Lago di Limides/Dolomites, Dotonbori Osaka neon, Erg Chebbi dunes, Black Forest mist, Cliffs of Moher, Iceland winter road, Stowe VT autumn, St Ives harbour, Grindelwald ridge hiker). Each is >=1920px wide, visually inspected, license-credited in its description; size_bytes updated to real sizes; filenames kept so PEER_MEDIA refs are unchanged.
  • Streaming route: new GET /api/peer-content/:onion/:content_id serving entry.disk via res.sendFile (Range/206 works — probed). Whitelist lookup only (T-hj1-03), paid entries return 403 so full paid bytes are unreachable without purchase. Demo nginx already proxies /api/ -> :5959; added the missing /api proxy to the vite dev config so dev works too.
  • Paid audio preview: content.preview-peer serves a ~10% leading slice of the real mp3 for audio entries (the Preview button plays it), images unchanged.

Task 2 — Shared viewer routing (commit f52c5407, both builds)

  • A8 fix + A7 routing: card click goes through openItem(): owned -> cached viewer/bottom bar; paid playable -> 10% preview; paid non-playable -> pay modal (image never fetched pre-purchase, T-hj1-04); free image -> existing Teleport-to-body viewer as a lightbox with viewerUrl pointed at the stream URL (no base64 round-trip); free audio/video -> playMedia (bottom bar / video modal).
  • Viewer footer: "Owned · unlocked" (green) only for owned items; "Free · shared by peer" (neutral) otherwise — also the bundle-grep sentinel. Save button streams free files (streamDownload) instead of calling content.owned-get.
  • Unified post-payment: payWithLightning, pollInvoice and pollOnchain now share openPurchased() with the ecash flow — mark owned, refresh owned list, audio autoplays in the bottom bar, image/video open in the viewer. triggerDownload remains for the explicit Save path.
  • Blob-URL hygiene: releaseViewerUrl() only revokes blob: URLs (free items use plain URLs).
  • Preview-thumbnail watcher deliberately NOT extended to audio (real nodes return audio bytes there). Cloud.vue/CloudFolder.vue verified to already open free images via MediaLightbox — untouched.
  • Regression test added: free image click opens the lightbox.

Task 3 — Verification

  • Full unit suite: 697/697 passed (baseline in plan said 195; suite has since grown — all green).
  • npm run build (vue-tsc + vite): success; grep -rl "Free · shared by peer" web/dist/neode-ui/assets/ hits PeerFiles-DsotBwvS.js (build did not no-op).
  • Mock RPC smoke (20 checks, all pass): browse-peer aggregation contains both Wavlake titles; paid purchase deducts 21 sats ecash and returns audio/mpeg >1MB; purchase appended to owned-list; /api/peer-content answers 206 to a Range probe, 403 for paid items, 404 for unknown/traversal ids; paid audio preview is audio bytes; image previews still jpeg.
  • grep -rn "146.59.87.168" neode-ui/mock-backend.js demo/ — empty (T-hj1-02).
  • Git: two focused code commits, nothing staged under indeedhub/, only other agents' pre-existing untracked files remain.

Deviations from Plan

1. [Rule 3 - Blocking] Session-memoised federation nodes. The plan assumed onions were "random per session"; they were actually random per RPC call, which would have made seeded owned-content onions never match what the frontend sees. Added sessionFederationNodes() memoisation (per-visitor via the existing session store). Commit 14d1a453.

2. [Rule 2 - Missing critical] /api dev proxy in vite.config.ts. The demo nginx proxies /api/ to the mock, but the vite dev server did not — the new route (and the existing /api/blob, /api/app-catalog) would 404 on :8100 dev. Added a /api proxy entry (dev-server-only config; real nodes use nginx). Commit 14d1a453.

3. [Minor scope choice] song-builders converted free -> paid (100 sats). The plan required a seeded audio purchase backed by real disk bytes, but the only paid audio item is the showcase Wavlake track, which must NOT be pre-owned (it would kill the A3 buy demo). Making one existing song paid provides a legitimately purchasable audio item for the seeded history and an Owned-badge example in the gallery.

Post-Deploy Live Checklist — http://146.59.87.168:2100 (orchestrator deploys)

  1. Search (A1/A2): Cloud -> search "Zazawowow" (or "WEBFIVE" / "Michael") — both tracks appear as peer-file results.
  2. Paid buy -> autoplay (A3): open the peer holding "Zazawowow - WEBFIVEFOURTHREETWOONE.mp3" (21 sats), Buy -> ecash -> Pay: the bottom GlobalAudioPlayer bar appears and the track audibly plays; card flips to green "Owned".
  3. Preview before buying (A3): the paid track's Preview button plays ~25s of real audio, not silence/artwork.
  4. Paid Files tab (A4): Cloud -> Paid Files shows 3+ seeded purchases with sats + dates; clicking "Builders, not talkers (Remastered).mp3" plays it in the bottom bar; the fresh Wavlake purchase from step 2 is now listed too.
  5. Dedupe (A5): browsing several peers, each file appears on ~1 peer; only 3 files (WEBFIVE track, Block Height film, aurora photo) appear on two.
  6. Real photos (A6): photo cards show real photographs (aurora, Dolomites lake, Osaka neon, dunes, mist, cliffs, Iceland road, Vermont autumn, St Ives, Grindelwald) — no picsum grey placeholders.
  7. Free image lightbox (A8): clicking any photo card opens the full-screen viewer with backdrop; footer reads "Free · shared by peer"; Save downloads it.
  8. Free audio/video (A7): clicking "Zazawowow - Michael Michael Saylor.mp3" (free) plays in the bottom bar; a free video (if on the browsed peer) opens the video modal.
  9. Paid gating (A7): a blurred paid image click opens the pay modal, never the image; paying via the Lightning QR path also opens the content in-app (no orphan browser download).

Self-Check: PASSED

  • demo/content/music/Zazawowow - WEBFIVEFOURTHREETWOONE.mp3 — FOUND (MPEG audio)
  • demo/content/music/Zazawowow - Michael Michael Saylor.mp3 — FOUND (MPEG audio)
  • demo/peer-media/artwork-webfive.jpg, artwork-michael-saylor.jpg — FOUND (JPEG)
  • All 10 demo/peer-media/photo-*.jpg — FOUND (JPEG, >=1920px wide)
  • Commit 14d1a453 — FOUND in git log
  • Commit f52c5407 — FOUND in git log