NSLibrary
Self-hosted library for your own Nintendo Switch dumps. Scan folders of NSP, NSZ, XCI, XCZ, and homebrew NRO files, fill in metadata, spot missing or duplicate content, send titles to a modded Switch over LAN, and back up its game saves.
There are no download sources, no shop scraping, and no title keys handed out. Keys (prod.keys) come from your own console, stay on the server, and are optional.
This project is for content you dumped yourself, from your own console and your own cartridges. It will not help you obtain games, keys, or firmware, and requests to add that are out of scope permanently. See CONTRIBUTING.md.
Features
- Library server — Fastify + SQLite. Walks library folders (read-only), watches for changes, and groups base games, updates, DLC, and homebrew.
- Web UI — React app served by the same process: library, homebrew, problems, devices, folders, and settings.
- Metadata — Container listings and tickets need no keys. Names, icons, firmware requirements, and NCA hashes need
prod.keysdumped from your console. Optional titledb (a URL or file you supply, refreshed daily if it's a URL) fills in names, which game DLC belongs to, latest-version numbers, and the release dates, languages, regions, age ratings, and player counts the library can be sorted and filtered by. A versions list alongside it adds update dates. - NSP → NSZ compression: the server compresses titles into an output folder you choose, usually 30 to 60% smaller. Each NSZ is read back and checked against the title's CNMT hashes before it's kept, and the page reports the space saved. With Prefer NSZ on, the Switch installs the compressed copy. Needs
prod.keys. - Device API — A paired Switch browses the catalog and claims install jobs. The console always initiates; “Send to Switch” queues work for it to pick up.
- Save backups: the Switch app backs up game saves to the server and restores them, on the same console or another you own. Only changed saves are uploaded, each save keeps its newest backups (pin the ones to keep forever), and the web UI downloads any backup as a tar. See docs/saves.md.
- Discovery — UDP
NSLIB?1on port 8466, plus mDNS_nslibrary._tcpso the server shows up in Bonjour/Avahi browsers. You can always type an IP by hand. - USB — A Switch plugged into the computer running NSLibrary (Electron, or Docker on Linux with device passthrough) uses the same device API inside
NSLUframes. Transfers time out so cancel and unplug can interrupt a job. - Desktop app — Electron wraps the same server: tray, native folder picker, USB, auto sign-in on this machine.
- Demo data — Synthetic containers and a fake keyset so the UI is usable without real dumps. No copyrighted content.
The Switch homebrew client (C++ / Borealis) streams NSP, NSZ, XCI, and XCZ installs into SD or NAND without copying the whole file to the SD card first. Connect over LAN HTTP or USB.
Quick start
Borealis is a submodule, so clone recursively (git submodule update --init --recursive if you already cloned):
git clone --recursive https://github.com/tuckerwales/nslibrary.git
cd nslibraryDocker (Node 22 image, linux/amd64 and linux/arm64, port 8465):
docker compose up --build
Open http://localhost:8465 and create the admin account. The first run asks for a setup token, which the server prints to its log on boot (docker compose logs nslibrary):
Setup token: K7M2X-9PQRT-4WHJN-6DFYB
Set NSLIB_SETUP_TOKEN to choose your own. Then browse the demo library, mount your dumps read-only as /library/games, and add that path under Folders.
Without Docker (Node ≥ 22.15, pnpm 12):
pnpm install pnpm seed NSLIB_SEED=true \ NSLIB_SEED_DIR="$PWD/data/demo-library" \ NSLIB_SEED_KEYS="$PWD/data/keys/prod.keys" \ pnpm start
Then pnpm --filter @nslib/web build so the server can serve the UI, or run pnpm dev:web alongside pnpm start / pnpm dev:server and use the Vite proxy on http://localhost:5173.
Production deploys (Coolify, volumes, PUID/PGID) are in docs/deploy.md.
Repository
pnpm workspaces. Shared types live in @nslib/shared; parsers never write into library folders.
| Path | Package | Role |
|---|---|---|
packages/shared |
@nslib/shared |
Zod schemas, error codes, USB frame constants, golden protocol files |
packages/formats |
@nslib/formats |
PFS0, HFS0/XCI, NCA, CNMT, NACP, ticket, NCZ, NRO, keyset |
packages/fixtures |
@nslib/fixtures |
Synthetic containers encrypted with a generated fake keyset |
packages/server |
@nslib/server |
HTTP APIs, scanner, SQLite (Drizzle), keys, titledb, UDP discovery |
packages/web |
@nslib/web |
React + Vite + TanStack Query UI |
packages/device-sim |
@nslib/device-sim |
CLI fake Switch for the device API (nslib-sim) |
packages/usb-host |
@nslib/usb-host |
USB frame session and node-usb attach |
packages/electron |
@nslib/desktop |
Electron window, tray, USB on the desktop |
switch/ |
— | libnx + Borealis client, host-native C++ tests |
docker/ |
— | Image entrypoint and compose overlay |
docs/ |
— | Device API, USB frames, keys, deploy |
Development
pnpm install pnpm test # Vitest across packages pnpm typecheck pnpm lint pnpm format pnpm dev:server # API + scanner on :8465 pnpm dev:web # UI on :5173, proxies /api to the server
nslib-sim talks to a running server the way a Switch would:
pnpm --filter @nslib/device-sim start -- pair --code 123456 pnpm --filter @nslib/device-sim start -- hello --token <token>
Switch client
Host-native tests (no devkitPro) cover PFS0, HFS0/XCI, NCZ, CNMT, tickets, JSON, the device-API codec, USB frames, and the install pipeline against the same golden files as TypeScript:
The .nro needs devkitPro (switch-curl, switch-libzstd). From the repo root:
./scripts/build-switch.sh
See docs/switch.md for pairing, USB, nxlink -s, and NSP/NSZ/XCI installs to SD or NAND. Host tests need libzstd-dev. Desktop: pnpm dev:desktop (see docs/desktop.md).
Configuration
| Variable | Default | Meaning |
|---|---|---|
NSLIB_HOST |
0.0.0.0 |
Bind address |
NSLIB_PORT / PORT |
8465 |
HTTP (web UI, /api/v1, /api/device/v1) |
NSLIB_DATA_DIR |
data |
SQLite, keys, icon cache, save backups. Docker: /data |
NSLIB_WEB_DIR |
packages/server/public or packages/web/dist |
Built UI; unset serves API only |
NSLIB_TRUST_PROXY |
off | Set true behind Coolify / Traefik |
NSLIB_POLLING |
off | Poll library folders (NFS/SMB) instead of inotify |
NSLIB_RESCAN_INTERVAL_MIN |
360 |
Rescan every folder this often, catching changes a watcher missed. 0 turns it off |
NSLIB_DISCOVERY |
on | 0 / false disables UDP discovery and mDNS advertising |
NSLIB_DISCOVERY_PORT |
8466 |
UDP port for NSLIB?1 |
NSLIB_SETUP_TOKEN |
generated | Creating the admin account asks for this value. Unset, the server generates one per boot and logs it, so a server reachable before you've signed up can't be claimed by whoever opens it first |
NSLIB_SERVER_NAME |
NSLibrary |
Shown in hello and discovery |
NSLIB_SEED |
off (on in the image) | Attach the demo library on first boot if no folders exist |
NSLIB_SEED_DIR / NSLIB_SEED_KEYS |
— | Demo library path and matching fake prod.keys |
PUID / PGID |
1000 |
Docker user that owns /data |
NSLIB_COMPRESS_THREADS |
cores − 1, at most 4 | Worker threads zstd uses when compressing NSP to NSZ |
NSLIB_LOG_LEVEL |
info |
Fastify log level |
NSLIB_TLS_KEY / NSLIB_TLS_CERT |
— | PEM files for optional HTTPS (LAN without a reverse proxy) |
NSLIB_NRO_PATH |
data/update/nslibrary.nro if present |
A signed release mirrored here, so consoles without internet can update (needs update.json and update.json.sig beside it) |
NSLIB_SAVE_MAX_MB |
1024 |
Largest save archive a Switch may upload. Backups live in <dataDir>/saves |
NSLIB_FORWARDER_MAIN |
data/forwarder/main if present |
ExeFS main for the HOME-menu NSP |
The server never writes into library folders. Missing files are marked, then purged after 30 days.
Forgotten password
Settings → Account changes the admin password and signs out every other browser. If you can't sign in at all, set a new one from a shell on the server; it works while the server is running and signs out every session:
# Docker docker compose exec -u node nslibrary node dist/main.js reset-password # From a checkout (uses NSLIB_DATA_DIR, default ./data) pnpm --filter @nslib/server reset-password
It prompts twice on a terminal, or reads the first line of stdin when piped
(echo "$NEW_PASSWORD" | …), so it can be scripted without the password appearing in ps.
Keys
Upload prod.keys in Settings (or PUT /api/v1/keys). Stored at <dataDir>/keys/prod.keys with mode 0600. The API reports which names are present; key material is never returned, logged, or sent to a device.
Without keys the library still lists files from containers, tickets, and filenames, and labels that metadata as unverified. Details: docs/keys.md.
Documentation
- Architecture — how the pieces fit together, and why
- Deploy — Docker image, Coolify, volumes
- Device API — pairing, catalog, jobs, Range downloads
- USB protocol — frame layout (same messages as HTTP)
- Save backups: backing up and restoring saves, the archive format, and its API
- Keys —
prod.keysand filename mode - Switch updates — one channel of signed
.nroreleases, fetched from GitHub or mirrored on your server
Contributing
Issues and pull requests are welcome — start with CONTRIBUTING.md, which covers setup, house style, and the things this project will not accept. Security vulnerabilities go through SECURITY.md rather than the issue tracker.
Licence
Apache License 2.0. Third-party components, including Borealis, TweetNaCl, libnx, the devkitPro portlibs, and the bundled Archivo font, are listed in THIRD-PARTY.md.
Legal
NSLibrary is an independent, unofficial project. It is not affiliated with, endorsed by, or associated with Nintendo. Nintendo Switch is a trademark of Nintendo. All product names, logos, and brands are the property of their respective owners, and are used only to describe what this software interoperates with.
NSLibrary is a file manager and a transfer tool. It ships no games, no keys, no firmware, and no
copyrighted Nintendo material of any kind; every test fixture in this repository is synthetic and
generated from scratch. It provides no way to obtain content, and does not circumvent any protection
measure — prod.keys must already be extracted from a console you own, and is optional.
What you do with it is your responsibility. Dumping content you own is treated differently in different countries; downloading or sharing content you do not own is not something this project supports, and running modified system software may violate Nintendo's terms of service and can get a console banned from online services.

