Privacy by Design: How re;file labs Uses WASM and Rust to Keep Your Files Local
Most online file tools follow the same pattern: you upload a file, a remote server processes it, you download the result. That's convenient, but it means your file touches infrastructure you don't control, operated by a company whose data retention and security posture you're trusting implicitly.
re;file labs is built around a different assumption: file processing should happen on your device, not on a server. This post explains how that works technically and what it means in practice.
1. Why Server-Side Processing Is a Privacy Risk
Uploaded files can be stored, logged, or intercepted at multiple points. Even services that claim automatic deletion have no reliable way to verify it for users.
Common concrete risks:
- Sensitive documents (contracts, IDs, invoices) processed on third-party infrastructure
- Metadata exposure: file names, sizes, and timestamps logged server-side
- Files retained longer than stated, or subject to a jurisdiction with weaker protections
re;file labs eliminates these risks by not transmitting files in the first place.
2. Local Processing: Everything Happens in Your Browser with WASM
The core of re;file labs is a local-first architecture built on WebAssembly (WASM). Instead of sending files to a server, we compile Rust libraries to WASM and run them inside your browser's sandbox.
This means:
- Your files never leave your device
- Processing happens without upload latency
- The browser sandbox isolates the WASM module from the rest of your system
Local Processing Flow
- You select or drag a file into the interface.
- The browser reads the file directly into memory.
- A Web Worker executes a WebAssembly module compiled from Rust, performing all conversions and edits locally.
- The processed file is offered for download from local memory. No data leaves the browser.
We use open-source Rust crates for image and document processing — the processing logic is auditable and not proprietary.
3. Architecture Overview
| Layer | Technology | Purpose |
|---|---|---|
| Browser Runtime | WebAssembly via Web Workers | Secure, sandboxed, multi-threaded execution |
| Core Libraries | Rust (image-rs, lopdf) | High-performance image and PDF processing |
| PDF Rendering | unpdf (npm) | Front-end PDF manipulation and rendering |
| Interface | Nuxt + TypeScript | Reactive UI |
| Communication | Message passing | Event-driven transfer between worker and UI |
| Storage | In-memory only | Data stays within the browser tab |
4. Performance
Eliminating uploads removes most of the latency in typical online file tools. The upload round-trip — send file, wait for server processing, download result — doesn't exist in a local-first model.
WASM execution runs at roughly 10–15% below native desktop speed for compute-heavy tasks. For most file operations (conversion, compression, metadata editing), that gap is imperceptible.
5. Privacy in Practice
| Concern | Typical web tool | re;file labs |
|---|---|---|
| File upload | Required | Never happens |
| Server logs | Possible | None (no server) |
| Account required | Often | No |
| Analytics | Common | Privacy-focused aggregate only (Swetrix) |
| Data retention | 24h–7d typical | Not applicable |
On analytics: we use Swetrix, a privacy-focused analytics service, to understand aggregate tool usage (which tools run, error rates). No file contents, no personal data, nothing that identifies you or your files is transmitted. Your browser's network panel will show the Swetrix request if you want to inspect it.
6. Developer Transparency
We believe privacy should be verifiable, not just promised.
The processing modules are open source. The Rust code that runs on your device is the same code in the repository — you can read it, compile it yourself, or audit it. We welcome community review.
Example: Core Image Conversion Logic
This simplified Rust function illustrates the data flow: bytes in, bytes out, entirely in local memory.
// Core image conversion function using the image-rs crate
pub fn convert_image(
file: &[u8],
src_mime: &str,
target_mime: &str,
) -> Result<Vec<u8>, ImageError> {
// 1. Load the raw file bytes into a DynamicImage structure
let img = image::load_from_memory(file)?;
// 2. Perform color-space adjustments as required by the target format
let processed_img = match target_mime {
"image/jpeg" => img.to_rgb8().into(),
_ => img,
};
// 3. Write the processed image to a new byte vector in the target format
let mut output: Vec<u8> = Vec::new();
let format = image::ImageFormat::from_mime_type(target_mime)
.unwrap_or(image::ImageFormat::Png);
processed_img.write_to(&mut Cursor::new(&mut output), format)?;
// The resulting byte vector is returned to the main thread for download.
Ok(output)
}
The full implementation is in the image processing repository.