nsz: lossless zstd compression for Nintendo Switch dumps, rebuilt on a different crypto backend
NSZ - Homebrew compatible NSP/XCI compressor/decompressor
At a glance
- What is it?
- A Python compression tool for NSP and XCI files that leaves every technological protection measure in place, survived a 958-day release gap, and came back five times faster because it switched AES libraries.
- Who is it for?
- nsz is a narrow tool with an unusually clear legal position: no keys shipped, no protection measures removed, compression applied to a file you already legitimately own. The 5.0.0 rewrite is the thing to know about, since it moved AES to PyCryptodome and fixed two classes of corruption in block and XCZ decompression that had gone unaddressed for nearly three years.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 45 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 7, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What the format does, and what it deliberately leaves alone
nsz compresses and decompresses Nintendo Switch game dumps losslessly, using the zstd algorithm. The compressed file can be installed with supported NSW Homebrew Title Installers, which is the whole point: the output is still an installable title, just smaller.
The README's legal section is unusually specific and worth reading before anything else. The project states that it does not incorporate any copyrighted material such as cryptographic keys, and that all keys must be provided by the user. It states that it does not circumvent any technological protection measures, and that the NSZ file format purposely keeps all technological protection measures in place. And it restricts use to legally purchased games or homebrew applications.
That combination is what makes the format different from repacking tools. Nothing is decrypted and re-encrypted, nothing is unlocked, and the result is byte-for-byte recoverable from the compressed file. The compression is applied to the parts of the file that are already compressible, and the protected regions are stored as they are.
The repository has 2,393 stars, 149 forks and 38 open issues, with the last push on 2026-08-23. It is hosted on GitHub with a GitLab mirror in Switzerland, which the README describes as the new home if GitHub ever removes the repository, and that is worth bookmarking given how often game preservation projects have moved hosts.
Keys: the one thing you have to supply
The requirements section is one paragraph long and unforgiving: you need a hactool compatible keys file in a suitable directory, and you must legally obtain your keys. There is no fallback, no bundled sample, and no partial mode.
The file must be named either `prod.keys` or `keys.txt`, and it must be in the directory you run the tool from or in one of a small set of per-platform locations:
| OS | Location | |---|---| | linux | `$HOME/.switch/`, `$XDG_CONFIG_HOME/nsz/`, `$HOME/.config/nsz/` | | macOS | `$HOME/.switch/` | | windows | `%USERPROFILE%/.switch/` |
A custom path can be given at runtime with the `--keys` parameter, which accepts either a direct path to the file or a directory containing `prod.keys` or `keys.txt`. The distinction matters in scripts: pointing at a directory and pointing at a file are both valid, and picking the wrong one fails late rather than at argument parsing.
Note the linux row carries three search paths while macOS and windows carry one. That asymmetry is real rather than a documentation oversight, and it means a Linux deployment following a Windows-oriented guide will look in the wrong place. In containers the same applies: the Docker example mounts the keys file into `/root/.switch/prod.keys` inside the image, which matches the linux `$HOME/.switch/` convention since the container runs as root.
Five installation paths, and the friction in each
Binaries for Linux, windows and macOS are on the releases page. The README is candid about the two friction points. On most Linux systems you have to right-click the file, open its properties, and mark it executable. On macOS the situation is worse, because Gatekeeper stops it: you must run `chmod +x nsz-cli-macos` or `chmod +x nsz-gui-macos` from a terminal, then go to System Settings, Privacy and Security, and allow the program.
Running from source needs Python 3.6 or newer. Install the CLI-only requirements or the CLI and GUI requirements, then run the entry script:
pip3 install -r requirements.txtpython3 nsz.pyThe pip package is published as `nsz`, with a GUI extra:
pip3 install --upgrade nszpip3 install --upgrade nsz[gui]There is an Android route too, which is unusual: install Pydroid 3 and its repository plugin, open the Pip tab, enter `nsz` with prebuild unselected, install, then use the `nsz` command from the terminal. The decompress flag is `-D`, so `nsz -D file.nsz` decompresses a file.
The Docker container is the newest addition. It advertises multi-architecture support for linux/amd64, linux/arm64, linux/arm/v7 and linux/ppc64le, an Alpine image of roughly 88MB, easy keys mounting, and shell-like usage.
docker run --rm -v "$(pwd)":/data -v "$HOME/.switch/prod.keys":/root/.switch/prod.keys nsz-tool:latest application.nspBehind the packaging is a small dependency set. `requirements.txt` lists three packages: `pycryptodome`, `zstandard` and `enlighten`, the last being a progress bar. That is the whole runtime surface for the CLI, and it explains why the 5.0.0 speedup was possible.
The 958-day gap and what 5.0.0 actually changed
The release history has a hole in it. Version 4.6.1 shipped on 2023-12-18, and 5.0.0 shipped on 2026-08-02, described in its own notes as the first official release in two years and eight months, or 958 days. The author apologises for the delay and promises more frequent releases.
The headline change is the crypto backend. The notes state that by using PyCryptodome's AES implementation, nsz became much faster and less resource intensive. That matters more than it sounds: compression and decompression of these dumps is dominated by AES work over the uncompressed regions, so swapping the implementation changes the wall-clock time of every operation.
Two classes of corruption bugs were also fixed. The notes say that in rare cases the earlier versions could produce corrupted output during block decompression and during XCZ decompression, and then make an important clarification: the NSZ and XCZ files themselves were unaffected and decompress correctly with 5.0.0. In other words, files produced by the old versions are safe; it was the decode path that could fail. For anyone with an archive of nsz files, that is the reassuring reading.
The rest of the changelog is ordinary maintenance: a None check when calling `ExtractTitleIDAndVersion` in `GameList`, a fix for a pipeline that broke on `VerificationErrors`, addition of the `master_key_11` key hash, a raw-string conversion for a regular expression, an updated `Keys.py`, a corrected read size in `BlockDecompressorReader`, and a new `IndependentNczDecompressorConcise`.
The `master_key_11` addition is the entry that affects users directly. Newer console firmware introduces new master keys, and without the corresponding hash a tool cannot process content protected by them. Adding it is what keeps the tool working against current hardware.
Verify, and the licensing metadata that disagrees with the files
The 4.6.0 release introduced a change worth understanding before you trust any output. `--verify` moved to file-level sha256 NSP hash validation, which guarantees the sha256 of the original file and of the decompressed file match, so the recreated file is bit-identical. Before that, verification was weaker.
In the same release, the XCI and XCZ handling was rewritten for multiple partitions. You can now keep all partitions including their content with `--keep`; by default the content of all but the secure partition is removed while the empty partitions themselves are kept, specifically for emulator compatibility. Several PFS0 header bugs were fixed in the same pass, including padding that did not follow the specification, an overlap between the header and the first file when `--remove-padding` was used, and handling of too-short header sizes.
That release also fixed a Kivy configuration problem in the GUI that left some users unable to start it at all, since older versions could corrupt the global Kivy configuration. 4.6.1, the day after, was a day-one fix for an `MPLUS1p-Medium.ttf` not-found error, and it automatically repairs a corrupted `default_font` property rather than only fixing the missing font. Two consecutive releases dedicated to a font bug is a fair illustration of the GUI's dependency surface.
On licensing, three sources disagree. GitHub reports the license as NOASSERTION, the README says the project is MIT licensed and links a `LICENSE` file, and the tree contains `LICENSE.md` rather than `LICENSE`, which means the README's own link points at a filename that is not there. The `setup.py` classifiers list the OSI MIT License, which agrees with the README. The practical resolution is that the MIT claim is well supported by the packaging metadata and the prose, while the metadata field and the broken link are both noise. Read `LICENSE.md` at the root rather than following the README link.
Editorial conclusion
nsz is a narrow tool with an unusually clear legal position: no keys shipped, no protection measures removed, compression applied to a file you already legitimately own. The 5.0.0 rewrite is the thing to know about, since it moved AES to PyCryptodome and fixed two classes of corruption in block and XCZ decompression that had gone unaddressed for nearly three years. Start with the release binaries, put your own `prod.keys` where the table says, and use the `--verify` flag until you trust the output, since bit-identical recreation is the property the tool is actually selling.
Frequently asked questions
What is an NSZ file on Nintendo Switch?
It is a Nintendo Switch game dump recompressed with the zstd algorithm, losslessly. The technological protection measures are deliberately left in place, so the file stays an installable title rather than a decrypted and repacked one, which is what lets NSW Homebrew Title Installers install it directly.
How to extract NSZ?
Run nsz with the decompress flag on the file, for example nsz -D file.nsz. You need a hactool compatible prod.keys or keys.txt in one of the documented locations, or pass a path with --keys. On macOS the release binaries also need chmod +x and a Gatekeeper approval in System Settings.
Does Ryujinx run NSZ files?
The README describes installing the compressed file with supported NSW Homebrew Title Installers, and separately mentions keeping empty XCI partitions for emulator compatibility, which points at Yuzu and its forks rather than at Ryujinx. The repository does not document emulator support directly, so check your emulator's own documentation for how it handles the format.
How can I convert NSZ files to NSP files on my Mac?
Use the macOS release binary and decompress the file, since nsz is lossless. chmod +x nsz-gui-macos or nsz-cli-macos from a terminal, then allow the program under System Settings, Privacy and Security. Your prod.keys has to sit in $HOME/.switch/ or be passed with --keys.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/nicoboss-nsz)