Hi all, I’m Shubham, an engineering student in Mumbai. For the last few months I’ve been building NO SUS, a Flutter app (web plus Android) for sharing documents and notes you don’t want sticking around. I’m the only developer, it’s Alpha and free right now, and the client is MIT on GitHub. So this is a maker post, and I’m mostly here for feedback.
What’s shipped:
- Burn Notes / Burn Files: encrypted in the browser with AES-256 (CTR for notes, CBC for files), no account on either side, files up to 25 MB, expiry 1h/24h/7d, and it opens once, then it’s gone. The key lives in the URL fragment.
- Go: open your Saved items on a borrowed PC (cyber cafe, college lab) by scanning a QR with the phone app, matching a 2-digit code and approving with fingerprint or face. The PC never gets your Google password or token.
- SecureSend: each viewer’s identity is watermarked on the document, with view limits, expiry, a view log and revocation.
- On Android, screenshots and screen recording are blocked with OS flags. On web that isn’t possible, so it’s touch-to-reveal blur plus the watermark: deterrence and attribution, not prevention.
Honest limit: SecureSend and group files are not end-to-end encrypted. They rely on Supabase RLS policies.
Stack: Flutter, Riverpod 3, supabase_flutter (Postgres + RLS, Deno edge functions), encrypt/pointycastle for the crypto, local_auth for the biometric step, pdfrx for viewing.
Three Flutter-specific things I learned the hard way:
-
Android App Links can’t match on the URL fragment. Every link the app mints looks like
https://<host>/#/burn/..., so the autoVerify intent filter can’t be path-scoped. Once the app is installed it catches every link to that host. If the native side doesn’t recognise a link shape, the link just lands on the home screen. There’s no browser fallback, because Android already chose the app. I ended up with one routing function that has to know every link shape the app can create, mirroring theUri.basehandling on web, plus tests that pin the old formats so links already out there keep working. -
ref.watch(provider.select(...))silently does nothing if your models don’t override==/hashCode. Every list emission creates new instances, so the selected value never compares equal and you rebuild anyway. There’s no error, the optimisation just turns into a no-op. I only noticed because a group detail screen rebuilt whenever any group changed. -
Multi-file burn shares: each file gets its own key/IV, and the encrypt → upload → confirm pipeline for each file runs in parallel with
Future.wait, with the AES work incompute(). On mobile that keeps the UI responsive. On web,compute()doesn’t get a separate isolate, so the heavy part still competes with the UI there.
Feedback I’d like:
- Has anyone moved heavy crypto off the main thread in Flutter web cleanly (a Web Worker, wasm, anything)? That’s the part I’d most like ideas on.
- Is there a better answer to the fragment + App Links problem than one big router that knows every link shape?
- If you try a burn link on web, does the flow make sense the first time?
Site: https://nosus.foo
Code: GitHub - https-shubhamsahu/NON_SUS · GitHub