A secure file sharing platform built with the MERN stack and Supabase Storage — with an optional client-side, zero-knowledge encryption layer on top of its token-based sharing model.
Cryptex allows users to upload files, organize them into folders, generate unique share tokens, and control access through public/private visibility settings. File contents are stored in Supabase Storage while metadata is managed through MongoDB. Any upload can also be encrypted entirely in the browser before it's sent — in that mode, the server only ever stores ciphertext and never sees the decryption key.
- AES-256-GCM encryption performed entirely client-side via the Web Crypto API
- Content, filename, and MIME type are all encrypted — not just the bytes
- The decryption key is generated in the browser and only ever travels inside a URL fragment (
#key=...), which browsers never send in HTTP requests — the server, its logs, and Supabase never see it - Encrypted files are always private, can't be renamed (the name is ciphertext), and can't be made public
- Trade-off, by design: if a share link is lost, the file is permanently unrecoverable — there is no server-side reset, because there's nothing on the server that could reconstruct the key
- A folder can be created as an encrypted vault — every file added to it is encrypted under one shared key, generated once in the browser at vault creation
- One link (
?token=...#key=...) unlocks the entire vault, rather than needing a separate key per file - Each file still gets its own random IV even under the shared key — required for AES-GCM's safety guarantee regardless of key reuse
- Vaults are always private and can never be made public, enforced server-side
- Uploading into a vault and uploading a standalone encrypted file use the exact same client-side encryption code — only which key gets used differs
- Known limitation: an existing plaintext (or differently-encrypted) file can't be added into a vault after the fact — vault membership is decided at upload time, not by moving files in later
- Upload files securely (with real magic-byte content verification for unencrypted uploads)
- Download files
- Preview supported files
- Rename files
- Delete files
- Create folders
- Organize files into folders
- Rename folders
- Delete folders
- Download an entire folder as a ZIP
- Unique share token generated for every file and folder
- Access shared content without exposing database IDs
- Easy and secure sharing mechanism
- Expiring tokens, with one-click regeneration to rotate a leaked token
- Public files/folders
- Private files/folders
- Toggle visibility anytime (encrypted files are private-only — see above)
- MongoDB stores metadata
- Supabase Storage stores actual files (ciphertext, for encrypted uploads)
- Express API handles uploads and access control
git clone https://github.com/Zephyrex21/Cryptex_File_Sharing.git
cd cryptexnpm installCreate a .env file and add the required values.
npm run devnpm startCreate a .env file in the root directory.
Example:
# MongoDB Atlas URI
MONGO_URI=
# Supabase Project URL
SUPABASE_URL=
# Supabase Service Role Key
SUPABASE_SERVICE_KEY=
# Supabase Storage Bucket Name
SUPABASE_BUCKET=
# Backend Port
PORT=3000| Method | Endpoint | Description |
|---|---|---|
| POST | /api/files/upload |
Upload file |
| GET | /api/files |
Get all files |
| GET | /api/files/:id |
Get file details |
| GET | /api/files/:id/download |
Download file |
| GET | /api/files/:id/preview |
Preview file |
| PATCH | /api/files/:id |
Rename file |
| PATCH | /api/files/:id/visibility |
Change visibility |
| DELETE | /api/files/:id |
Delete file |
| GET | /api/files/token/:token |
Access via token |
POST /api/files/uploadaccepts an optional encrypted-upload contract: multipart fieldsencrypted=true,iv,encryptedName,encryptedNameIV,encryptedMimeType,encryptedMimeTypeIValongsidefile(sent as ciphertext,application/octet-stream). Seecontrollers/fileUpload.jsandpublic/js/app.jsfor the client/server contract.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/folders |
Create folder |
| GET | /api/folders |
Get folders |
| GET | /api/folders/:id |
Folder details |
| PATCH | /api/folders/:id |
Rename folder |
| PATCH | /api/folders/:id/visibility |
Change visibility |
| DELETE | /api/folders/:id |
Delete folder |
| POST | /api/folders/:id/files |
Add file to folder |
| DELETE | /api/folders/:id/files/:fileId |
Remove file from folder |
| GET | /api/folders/token/:token |
Access via token |
Standard upload:
- User uploads a file.
- File is stored in Supabase Storage.
- Metadata is stored in MongoDB.
- A unique token is generated.
- Users can share the token to provide access.
- Visibility settings determine whether content appears publicly.
Encrypted upload:
- User toggles "End-to-end encrypt" before uploading.
- The browser generates a random AES-256-GCM key and encrypts the file's content, filename, and MIME type — nothing plaintext leaves the device.
- Only ciphertext is uploaded and stored in Supabase; MongoDB stores ciphertext blobs for the name/type too, not the real values.
- The file is forced private and given a token, same as above.
- The share link is built as
.../app?token=<token>#key=<key>— the#key=fragment is never transmitted to the server by the browser, so this is the only place the key exists outside the uploader's own session. - Whoever opens that link decrypts the file locally in their browser; the server is never asked to, and couldn't if it were.
- Sensitive credentials are stored in environment variables.
- Database IDs are never exposed for sharing.
- Supabase Service Role Key remains server-side.
- Private files can only be accessed through their token.
- Uploaded file content is verified against its declared type using magic-byte signatures, not just the client-reported MIME type (see
verifyMagicBytesincontrollers/fileUpload.js). - Unencrypted uploads get a best-effort malware pre-screen via VirusTotal's hash-lookup API — see
utils/malwareScan.jsfor exactly what this does and doesn't catch (it's a known-hash lookup, not a full scan, and fails open if VirusTotal is unreachable or no API key is configured). - Encrypted uploads are zero-knowledge by construction, not just policy: the AES key never appears in any request body, response, log line, or database row this server controls. This also means magic-byte content verification is skipped for encrypted uploads — the server has no plaintext to check, which is the expected cost of not being able to read the file at all.
- Known limitation: encryption happens at upload time only. There's currently no way to encrypt a file that's already been uploaded in plain — that would require downloading, encrypting, and re-uploading it, which isn't implemented yet.
- Known limitation: encrypted files are excluded from folder ZIP downloads (bulk decryption would need a folder-level key exchange, which is a separate future feature, not a silent omission).
Built by Saurabh Raj Shekhar using MERN Stack and Supabase Storage.
This project is licensed under the ISC License.