RARxDOpen an archive

What this is

An archive tool that never sees your files.

RARxD opens RAR, 7z, ZIP and TAR archives, lets you rearrange what’s inside them, and builds a new archive from the result. All of it happens in the browser tab you’re reading this in. Nothing is uploaded, nothing is stored on a server, and there is no account to make.

That last part is the whole point. Most “open your RAR online” sites work by taking a copy of your file first. If the archive holds a contract, a passport scan or anything else you’d rather not hand to a stranger, that’s a real cost for a small convenience.


When you’d reach for it

Someone sent you a .rar and you’re on a locked-down machine

Work laptops often won’t let you install WinRAR or 7-Zip. A browser tab needs no permission from anyone.

You need to send one file, not forty

Drop files in, build a ZIP, done. There’s a draft mode for exactly this: give it files that aren’t an archive and it offers to make one.

The archive is nearly right, but not quite

Drop the 400 MB of raw video before you forward it, rename the folder someone typo’d, move things into a sensible shape — then repack. You never unpack to disk and repack by hand.

It has to fit under an attachment limit

Split the output into numbered parts at 25 MB, 100 MB or 1 GB. Rejoin them on the far end with cat name.zip.* > name.zip.

It needs a passphrase on it

ZIP output can be encrypted with AES-256, which 7-Zip, WinZip and WinRAR all read. See the limits below before you rely on it.

There’s an archive inside the archive

Open it in place, edit it, and it’s repacked into its parent on the way back out.


Formats

Reads — 20 formats

RARRAR57-ZipZIPTARTAR.GZTAR.BZ2TAR.XZTAR.ZSTGZIPBZIP2XZZSTDZ (compress)ISO 9660CABLZHARJCPIOAR

Writes

ZIPTARTAR.GZ7ZTAR.XZTAR.ZST

RAR is read but never written, and never will be — the format is closed and its compressor isn’t ours to reimplement. Open a RAR, edit it, and the result comes out as something else.

LZ4 is recognised but not decoded — the reader in this build has no decompressor for it. You’ll be told that rather than left with a failure.


Under the hood

For anyone who wants to know what’s actually running. The short version: two WASM decoders, a streaming writer, and a deliberate refusal to hold whole archives in memory.

Formats are identified by their header, not their name

An extension is a claim, not a fact. Every file is read by magic number, so a ZIP saved as .rar opens as a ZIP and the app says so rather than failing at the decoder. Where a name and its contents disagree, the contents win.

Two decoders, because one of them can't do the job

Most formats go through a WASM build of libarchive. RAR is routed to node-unrar-js, the RARLAB reference decoder, because libarchive cannot decrypt RAR — which was established by testing it rather than by reading about it.

A third libarchive, compiled here, fills two gaps

The decoder the app reads with ships read-only — no archive_* symbols in its glue at all — so writing 7z, TAR.XZ and TAR.ZST needed a libarchive built for this project, in public/writer/. It also turned out to have been built without zstd, which meant the app could produce .tar.zst files it could not then reopen; the same build carries a decompressor for exactly that. It is otherwise additive — if it fails to load, those formats disappear and everything else carries on.

Its filename can't tell you which build it is

The WASM module and its JavaScript loader are a matched pair, and both live at stable paths. A cache that keeps one and replaces the other produces a failure several frames deep inside Emscripten with nothing useful to say. Every asset URL therefore carries a hash of the binary, generated at build time, and the app checks its exports after loading so a mismatch names itself.

Nothing accumulates that doesn't have to

Compression runs in a worker and writes each block straight to the file you picked, via the File System Access API, so a 4 GB repack costs no more memory than a 4 MB one. Large archives load their entries on demand behind an LRU cache rather than up front. Splitting rotates output files mid-stream rather than buffering and slicing.

Paths are identity, which is harder than it sounds

Selection, open folders and the undo stack are all keyed by path, and renaming exists to change paths. Every edit therefore returns the moves it made, and one code path walks selection, expanded folders and the range-select anchor through the same remapping. Links move too: hardlink targets are archive-internal paths and follow directly, while relative symlink targets are resolved against the old location and re-derived from the new one — so a link survives either end being moved.

The cipher is hand-written, for one specific reason

WebCrypto has AES-CTR and it is the wrong CTR. NIST counter mode increments the last bytes of the counter block as a big-endian integer; WinZip AES increments the first eight as a little-endian one, and no arrangement of WebCrypto’s parameters produces the other convention. So the block cipher is ours. Key derivation and the authentication tag still aren’t — those are PBKDF2 and HMAC from WebCrypto, where they belong.

Output is verified by tools that didn't write it

Every archive the packer produces is checked against something with no shared code: system unzip, GNU tar, py7zr, pyzipper and the zstandard bindings. A library reading its own output proves very little — that’s how a padding bug that broke every zstd reader in existence was caught, and how a single wrong byte in an AES S-box was caught after it had passed a published test vector.


What it won’t do


Built as a client-side app in Next.js and TypeScript. There is no backend, no telemetry and no analytics — not as a policy, but because there is nowhere for anything to go. Close the tab and the workspace is gone.

Open an archive