Share files securely.
Try it: https://partage.deltablot.com
- client-side encryption with passphrase
- server knows nothing about the uploaded file
- extremely lightweight and fast
- made with go + html + vanilla js
The idea behind Partage is to have something that gets straight to the point, with no extra features, which means some choices have to be made.
No extra fonts, no runtime libraries, no i18n, simple and straightforward interface and codebase.
It is a very simple service to deploy, with a single container of about 10 Mb. There are no database, nothing fancy to setup. You launch the container and it runs.
We believe in lean software, meaning that we only add what is necessary. In the case of Partage, there are no runtime javascript dependencies, and no go dependency. The whole source code can be audited easily, because it's so small.
The css and javascript assets are minified and served with brotli compression. The whole application is only a few kb. That's a small fraction of the size of files loaded by other similar solutions.
If you're interested in having a file sharing service that is fast, easy to deploy, secure and pleasant to use, then use Partage.
Give it an svg file URL with --build-arg SVG_LOGO_URL=https://example.com/logo.svg to inject a different logo at build time.
The application is configured through environment variables.
The only required variable is SITE_URL, which corresponds to the public URL of your service.
Configuration variables:
| Name | Description | Default | Example |
|---|---|---|---|
SITE_URL (required) |
the full URL of the site, as it appears to visitors | http://localhost | https://partage.deltablot.com |
MAX_FILE_SIZE_MB |
maximum size of uploaded files in Mb | 1024 | 2048 |
MAX_TOTAL_FILES |
maximum number of uploaded files | 24 | 1337 |
CLEANUP_TIMER_MIN |
interval of time in minutes between pruning of old | 10 | 60 |
By default, the program listens on port 8080. You need to have a reverse proxy in front of it, terminating TLS, as it only listens in HTTP, but the app requires access through HTTPS.
# quick and dirty on localhost (no persistence)
docker run -p 8080:8080 -e SITE_URL=http://localhost:8080 --rm --name partage ghcr.io/deltablot/partage
# or with a volume for persistence
mkdir files
# this id/gid corresponds to nobody user in most cases
# it is the userid running inside the container
sudo chown 65534:65534 files
# expose this service through a TLS terminating reverse proxy
docker run -e SITE_URL=https://partage.example.com --rm --name partage -v $(pwd)/files:/var/partage ghcr.io/deltablot/partageSee docker-compose.yml example file.
You can use the VERSION build argument to customize the version string.
docker build --build-arg VERSION=custom -t ghcr.io/deltablot/partage .
TL;DR: encryption happens locally, the server doesn't know anything about the files you share.
Before being sent to the server, the file is locally encrypted with a key derived from the provided passphrase. It is then sent to the server, with some encrypted metadata such as the original file name. This encryption is done using the SubtleCrypto API. This means no external library is used, we only rely on browser implementation. Partage is only available in secure contexts, which is localhost and https.
To download a file, the correct passphrase must be provided, along with the filename which is present in the hash part of the URL − not sent to the server. The encrypted file is requested from the server, and decrypted locally with the provided passphrase.
Uploads use a chunked encrypted format:
header + encrypted metadata + encrypted file chunks
The authenticated header contains a format marker, a 16-byte PBKDF2 salt, an 8-byte random IV prefix, the chunk size, and the encrypted metadata length. The metadata is a JSON object containing the original content type, creation date, filename, size, and optional shared text.
The passphrase-derived AES-256-GCM key is created once per upload. Metadata is encrypted as chunk 0, then the file is encrypted in 4 MiB chunks. Each chunk uses a unique 12-byte IV composed of the random 8-byte prefix followed by a 32-bit chunk counter. The complete header is supplied as AES-GCM additional authenticated data, so modifying format parameters, reordering chunks, truncating a file, or appending data causes authentication or size validation to fail.
Large encrypted uploads are staged in the browser's origin private file system (OPFS) before being passed to FormData. This keeps JavaScript memory bounded to roughly one plaintext chunk plus one ciphertext chunk instead of creating several whole-file copies. Small uploads stay in memory. Large uploads require OPFS so Partage does not silently fall back to allocating the whole ciphertext in RAM; the temporary OPFS ciphertext is removed after the upload finishes.
This way, the server still has no knowledge about the original filename or contents. On the server, the opaque encrypted file is stored with a 72-bit random identifier encoded as 12 URL-safe Base64 characters, suffixed with the expiration Unix timestamp encoded in base36. The same compact identifier is used in the share URL. Periodically, the program removes files that have expired based on the timestamp stored in the filename.
On download, files are authenticated and decrypted one chunk at a time, then exposed to the browser as a Blob for download.

