I built a P2P file sharing app using Nostr for peer discovery.
How it works: Log in with your Nostr extension, pick a recipient by npub, select a file, and send. WebRTC signaling goes through Nostr relays (encrypted with NIP-44), then files transfer directly browser-to-browser. The relay never sees your files.
Try it: https://k4lb1.github.io/Zoop/Code: https://github.com/k4lb1/Zoop
Relays only handle tiny encrypted signaling messages - the actual file goes peer-to-peer.
What I learned: Nostr’s event system works surprisingly well for WebRTC coordination. NIP-44 encryption keeps signaling private. ICE negotiation on mobile networks is still brutal though.
Thinking about a desktop version with persistent transfer history and contact management.
Feedback welcome - especially if you find bugs.
That's great, forked! Not sue which kind of issue I'm facing and it is not loading properly. Anything should I set to fix?
Thanks for forking! Can you tell me a bit more? do you see any errors in the browser console?
Sure, I simply forked and deployed in GH pages using the existing
workflows/deploy.ymlhere a screenshot
Hey, sorry for the hassle.
The 404 happens when Pages is set to “Deploy from a branch” with the main branch and the root (/) folder. In that case GitHub serves the raw repo (your source code), but the actual site is built by Vite into a dist/ folder. The root only has things like src/, package.json, etc., and no index.html, so the browser gets a 404.
What to do: In your fork go to Settings → Pages. Under Build and deployment, set Source to GitHub Actions (instead of “Deploy from a branch”). Then the workflow runs on every push to main: it runs npm run build and deploys the built dist/ folder. Give it a minute or two after a push, then https://4g0r4.github.io/Zoop/ should load.
I’m not that experienced with Git and GitHub myself, so I only figured this out with some help. We’ve added a “Quick Start for Forks” section in the README with these steps so it’s easier next time. Hope that fixes it for you.
That's how is currently set: but in the log I can see there's no /dist folder built
Run npm run build > zoop@0.1.0 build > tsc -b && vite build vite v7.3.1 building client environment for production... transforming... ✓ 158 modules transformed. rendering chunks... computing gzip size... dist/index.html 1.14 kB │ gzip: 0.64 kB dist/assets/index-BOgfiTc6.css 6.30 kB │ gzip: 1.94 kB dist/assets/ErrorBoundary-Dk8HoutI.js 0.85 kB │ gzip: 0.52 kB dist/assets/index-D6ovzhIu.js 145.35 kB │ gzip: 47.27 kB dist/assets/App-DA9VGWZa.js 263.91 kB │ gzip: 87.84 kB ✓ built in 2.51sHave also updated the fork and the errors are not coming up anymore. Just
Loading Zoop…but is still not loading.Hey, thanks for trying it out and for reporting this.
The repo doesn’t ship a dist/ folder on purpose (it’s in .gitignore), so you need to build it once before running. I updated the README.md to make that clearer.
To run it locally: after npm install, run npm run build (creates dist/), then npm run preview. Or simply npm start – that builds and starts the preview server in one go.
For development (with hot reload): just npm run dev – no build needed.
For GitHub Pages: in your fork, set Pages source to GitHub Actions (not “Deploy from a branch”). The workflow will build the app and deploy it; no local dist/ required.
Hope that fixes it. Let me know if something still doesn’t work.
I found the issue... it was just the name of the repo, as I rename it all lowercase 'zoop'. Renaming it to 'Zoop' solved the issue. Thanks for your help, always learning
https://4g0r4.github.io/Zoop/
Now getting a new error:
Another question, how the receiving npub get notified or know about the fact that now can download the file?
Nice work — using Nostr relays for WebRTC signaling is clever. NIP-44 encryption for the signaling messages is the right call since those contain ICE candidates that could leak IPs.
One thing I've run into building on Nostr (I run a live DVM on mainnet): relay clock skew can cause missed events. If you're using
sincefilters to only grab recent signaling messages, add a 15-30 second lookback window. I lost requests early on because my subscription filter was too tight and relays had a few seconds of clock drift.For the mobile ICE issues — have you tried including a TURN fallback? Symmetric NAT on cell networks blocks most peer-to-peer connections. Metered.ca has a free TURN tier that works well enough for signaling-only setups.
Thanks for the detailed feedback. I already using NIP-44 for signaling and a TURN fallback (Metered). I'll look into adding a short lookback window for the since filter to handle relay clock skew. Appreciate it.