DEV Community

FileShot
FileShot

Posted on

How I built zero-knowledge file sharing where the server is literally blind to your files

Most file-sharing tools work like this: you upload a file, it sits on their server in a format they can read, and you share a link. The company has your file. Their employees, their compliance team, their government requests — they all have access to your content.

I wanted to build something different. I wanted a file-sharing tool where — technically, provably — the server has zero ability to read what you've sent.

That's FileShot.io.

The core idea: encrypt before upload, key stays in the URL fragment

The way it works:

  1. You pick a file in your browser
  2. The browser generates a random AES-256-GCM key
  3. The file is encrypted locally using the Web Crypto API
  4. The encrypted blob is uploaded to the server
  5. The key is appended to the URL fragment (#key=...)

That last point is the critical one. The URL fragment — the part after # — is never sent to the server in HTTP requests. It lives only in the recipient's browser.

When someone opens the share link, their browser:

  • Reads the key from the fragment
  • Fetches the encrypted blob from the server
  • Decrypts it locally using Web Crypto
  • Offers the file for download

The server stores encrypted noise. It has no key, no ability to decrypt, no idea what the files contain.

Zero signups. No account. Ever.

One thing I was firm on: the core file sharing functionality requires zero signup. You land on the page, drag a file in, get a link. That's it.

There's no login wall before you can share. No email confirmation. No account required on the receiving end either. It just works.

The free tier is extremely generous: 500MB per file, links stay live for 7 days. You can share files immediately with absolutely no commitment.

If you want longer-lived links (30 days, 90 days, or permanent) and larger file sizes, there's a paid tier — but I deliberately made the free tier good enough for the vast majority of use cases.

The technical implementation

Encryption (client-side only)

// Generate a fresh key for every upload
const key = await crypto.subtle.generateKey(
  { name: "AES-GCM", length: 256 },
  true,
  ["encrypt", "decrypt"]
);

// Encrypt the file
const iv = crypto.getRandomValues(new Uint8Array(12));
const encrypted = await crypto.subtle.encrypt(
  { name: "AES-GCM", iv },
  key,
  fileBuffer
);

// Export and base64-encode the key for the URL
const rawKey = await crypto.subtle.exportKey("raw", key);
const keyB64 = btoa(String.fromCharCode(...new Uint8Array(rawKey)));
Enter fullscreen mode Exit fullscreen mode

The encrypted blob and IV are bundled together, then uploaded to the server. The key never leaves the browser — it goes straight into the URL fragment.

Key in fragment = server blind

// Share URL example:
// https://fileshot.io/share/abc123#AES256KEY_BASE64_HERE

// When recipient opens the link:
const fragment = window.location.hash.slice(1);
const keyBytes = Uint8Array.from(atob(fragment), c => c.charCodeAt(0));
const key = await crypto.subtle.importKey("raw", keyBytes, "AES-GCM", false, ["decrypt"]);
Enter fullscreen mode Exit fullscreen mode

The server receives requests to /share/abc123. It has no idea what #AES256KEY_BASE64_HERE is — the browser never transmits it.

Server: just a storage layer

The backend is a Node.js/Express server. Its job is simple: store encrypted blobs, serve them on request, enforce expiry, handle rate limiting. It has no decryption logic whatsoever. There's nothing to decrypt with.

Transparency

The entire encryption/decryption implementation is published in the open. Anyone can audit the client-side code in their browser DevTools. The ZKE transparency page at fileshot.io/zke explains the architecture in detail.

What this means in practice

  • If FileShot's servers were breached — the attacker gets encrypted blobs. Useless without the keys.
  • If FileShot receives a legal demand — we can hand over everything we have: encrypted data we can't read. The key is not on our servers.
  • No account means no identity — your files aren't tied to an email address by default.

Tools and desktop app

There's also a desktop app (Windows/Mac) that integrates with your clipboard — screenshot something, automatically uploads and puts the share link in your clipboard. The encryption is identical.

A Chrome extension does the same: right-click any image or selection on any page, get a FileShot link instantly.

All of them use the same browser-side AES-256-GCM encryption. The desktop app uses the Electron renderer's Web Crypto API. Same keys, same process.

What's free

  • 500MB per file
  • 7-day link lifetime
  • Unlimited uploads
  • No account required
  • Full AES-256-GCM encryption on everything

Try it: fileshot.io

The entire thing was built solo. If zero-knowledge file sharing is useful to you — whether for sending sensitive documents, sharing files with clients, or just not wanting a corporation sitting between you and your recipients — I'd love your feedback.


Built with: Web Crypto API (AES-256-GCM), Node.js, Express, Electron, Chrome MV3 extensions

Top comments (0)