Monthly Grant Report - June 2026
Project: PrivRec
What progress was made on your grant this month?
This month PrivRec went from initial commit to a working end-to-end app covering nearly all of Milestone 1. The full pipeline now functions entirely client-side: a user onboards with a BIP-39 recovery phrase, records their screen or camera in the browser, encrypts and uploads the recording directly to Sia via the Sia JS SDK, picks a link expiry, and gets a shareable link. Recordings stream back out of Sia into a browser video player using MediaSource for progressive playback, with a full-buffer blob fallback for browsers without MSE.
Alongside the features, I invested in security and code quality: the Sia App Key is encrypted at rest with a non-extractable AES-GCM key held in IndexedDB (only ciphertext touches localStorage), the production build ships a strict Content-Security-Policy, and share/approval URLs are validated against dangerous schemes. The project is fully buildable, lint-clean, and covered by 30 passing unit tests.
I was able to record a multi-minute screen capture, encrypt and upload it to Sia, generate an expiring share link, and stream it back in a fresh browser tab — confirming the record → encrypt → upload → share → play loop works in practice.
Detail of tasks worked on this month per milestone
| Milestone | Tasks | Pull Request(s) |
|---|---|---|
| Onboarding & indexer | Vite + React 19 + Tailwind v4 SPA scaffold with WASM init and auth-gate routing; Sia JS SDK integration wrapper (lib/sia.js) for connect, approval, reconnect, upload, and share; onboarding flow with recovery-phrase generation/entry, custom indexer support. |
#1, #2, #3 |
| Login | Returning-user login via stored App Key; phrase re-entry on a new device re-derives the same key | #2, #3 |
| Encrypted key storage | At-rest App Key encryption with a non-extractable AES-GCM key in IndexedDB (lib/keyStore.js); recovery phrase never stored |
#2 |
| Recording | Camera-only and screen-only modes via MediaRecorder (hooks/useRecorder.js) with runtime codec detection, camera and microphone device selection |
#4 |
| Upload & share | Upload to Sia with user-selected expiry (1h/24h/7d/30d/Never) stored in encrypted object metadata; share-link generation via SDK shareObject() with native expiry |
#5 |
| Playback | Recipient playback page with progressive streaming via MediaSource, a full-buffer blob fallback, and friendly error mapping for expired/invalid links | #6 |
| Security & quality | Strict production CSP, security headers (frame-ancestors, nosniff), URL scheme validation, and open-redirect protection; 30 passing unit tests, ESLint clean, and lib/ module organization |
#1, #2, #5, #6 |
Were there any problems encountered this month?
- Browser codec variance. MediaRecorder codec support differs across browsers; mitigated by detecting supported MIME types at runtime and falling back to WebM/VP8/Opus.
What will you be working on next month?
- Implement the no-account recipient playback flow
- Recording history dashboard with share buttons
- Picture-in-picture recording mode (camera + screen combined)
- Regenerate a new share link for any existing recording
- Manual deletion of recordings
- Basic account stats from the Sia JS SDK account API
- User testing and feedback