Privacy by Design: How re;file labs Uses WASM and Rust to Keep Your Files Local
Modern file tools often demand a tough trade-off: convenience for your privacy. Most online converters and editors require you to send your files to a remote server for processing, which means trusting someone else's infrastructure with your sensitive data.
At re;file labs, we built our tools to eliminate that trade-off entirely. We believe you should have instant, powerful file utilities without risking your security.
This post details how re;file labs works, why your files never leave your browser thanks to WebAssembly (WASM) and Rust, and what makes this approach demonstrably faster and safer than traditional server-based tools.
1. Why Server-Side Processing Is a Privacy Risk
Most file utilities today follow a simple pattern:
- You upload your file.
- A server processes it.
- You download the result.
That seems fine at first, but each step introduces risk. Uploaded files can be stored, logged, or intercepted. Even when services claim to delete files automatically, there's no reliable way to verify it.
Common privacy concerns:
- Sensitive documents (contracts, IDs, invoices) being processed remotely
- Metadata exposure (file names, sizes, timestamps)
- Temporary uploads that linger on third-party servers
re;file labs eliminates these risks entirely.
2. Local Processing: Everything Happens in Your Browser with WASM
At the core of re;file labs is a local-first architecture powered by WebAssembly (WASM). Instead of sending files to a distant server, we compile highly optimized Rust libraries directly to WASM and run them securely in your browser's sandbox.
This means:
- Your files never leave your device
- Processing happens instantly without upload latency
- You can even go offline mid-session and continue working
Local Processing Flow
- The user selects or drags 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 straight from local memory — no data ever leaves the browser.
re;file labs builds on the shoulders of giants, using open-source Rust crates and proven libraries for image and document processing. This foundation allows us to focus on performance, reliability, and privacy — without depending on any external servers.
No external API calls.
No storage.
No logs.
Just local computation. (A "Zero Trust" model.)
3. Architecture Overview
| Layer | Technology | Purpose |
|---|---|---|
| Browser Runtime | WebAssembly (WASM) via Workers | Secure, sandboxed, multi-threaded execution |
| Core Libraries | Rust (image-rs, rawloader, lopdf) | High-performance Image and PDF processing |
| PDF Rendering | unpdf (npm library) | Front-end PDF manipulation and rendering |
| Interface | Nuxt + TypeScript | Reactive, minimal user interface |
| Communication | Message passing | Event-driven data transfer; no remote endpoints |
| Storage | In-memory only | Ensures all data stays within the browser |
This stack is built entirely on Rust for native-level performance within the browser. We leverage Rust crates like image-rs and rawloader for fast image handling, and lopdf for complex PDF operations. For front-end PDF tasks, the unpdf NPM library is used directly.
Crucially, all our compiled Rust code runs as WebAssembly modules and is executed by Web Workers. This multi-threaded worker model provides secure, sandboxed execution that's isolated from the main UI thread, delivering near-native performance without compromising the responsiveness or security of your browser session. Nothing ever leaves your browser.
4. Performance Benchmarks
To validate the local-first model, we benchmarked conversions using standard formats.
| Task | Traditional Web Converter | re;file labs (Local) |
|---|---|---|
| PNG → WebP (1 MB) | 2.4 s (upload + process) | 0.3 s |
| JPEG → PNG (2.5 MB) | 12.1 s | 4.5 s |
| PDF text extraction | 8.5 s | 0.7 s |
These benchmarks were conducted using the first search result from Google for each file type conversion.
Observations
- Eliminating uploads removes most of the waiting time.
- WASM execution performance is typically only slightly (10–15%) slower than native desktop speed.
- CPU load remains minimal, even during heavy tasks.
5. Privacy by Design
Privacy at re;file labs is not an option or mode; it's the foundation of how each tool works.
| Concern | Typical Web Tool | re;file labs |
|---|---|---|
| File upload | Required | Never happens |
| Server logs | Possible | None |
| Account data | Often required | Not used |
| Cookies & analytics | Common | Disabled |
| Data retention | 24h–7d typical | Not applicable |
Even our website infrastructure follows minimal tracking principles. There are no analytics pixels, session trackers, or embedded third-party scripts. You can inspect your browser’s network panel and confirm that nothing is transmitted beyond static assets.
6. Developer Transparency
We believe privacy should be verifiable, not just promised.
Our open repositories on GitHub show how the processing modules work and how data flows through the system. We welcome community audits and contributions to ensure our privacy claims hold up under scrutiny.
Furthermore, we publish these Rust-based modules both as WASM (npm packages) and native Rust crates, so developers can build their own local-first tools using the same privacy-preserving principles.
Example: Core Image Conversion Logic
This simplified Rust function, which runs inside the Web Worker's WASM module, illustrates the fundamental, three-step data transformation - all using bytes held locally in memory: The actual code is more complex and can be found in our image processing repository.
// 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 image processing/color-space adjustments (e.g., JPEG requires RGB)
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 passed back to the main thread for download.
Ok(output)
}