tinfoil-bolt is a lightning-fast, zero-dependency, and lightweight backend server to serve your personal game backup library to Tinfoil on Nintendo Switch.
Built with Bun, it replaces bloated, unmaintainable alternatives with a clean, modular TypeScript codebase that does exactly what you need and nothing else.
Many existing Tinfoil server solutions suffer from feature creep: auto-updaters that pose security risks, heavy polling mechanisms, required databases, or complex UIs.
tinfoil-bolt is different:
- Zero Dependencies: No
node_modulesblack hole. Uses Bun's native HTTP and FileSystem APIs. - Stateless: No database to sync. It scans your folders when Tinfoil asks.
- Multi-Mount Support: Seamlessly serve games scattered across multiple drives/mounts (e.g.,
/mnt/nasand/mnt/usb) as a single unified library. - Secure: Designed to run with Read-Only access to your files. No risk of deletion or corruption.
- Performance: Uses the
sendfilesyscall (via Bun) for high-performance file streaming from your NAS to your Switch.
This is the easiest way to run tinfoil-bolt on your NAS, Home Server, or Proxmox LXC container.
- Clone or download this entire repository.
- Copy
.env.exampleto.envand update with your game directories and optional auth settings. - Update the volume paths in
docker-compose.ymlto match your actual game storage locations. - Run:
docker compose up -dIf you are running a raw LXC container and don't want Docker overhead:
- Install Bun:
curl -fsSL https://bun.sh/install | bash- Configure:
cp .env.example .env
# Edit .env with your game directories- Run:
bun run src/server.ts
# or with npm-style scripts:
bun startCopy .env.example to .env and configure the following variables:
| Variable | Description | Default |
|---|---|---|
PORT |
The port the server listens on. | 3000 |
GAMES_DIRS |
Comma or semicolon-separated list of absolute paths where your NSP/NSZ/XCI files are stored. | /data/games |
CACHE_TTL |
Cache duration (in seconds) for shop data. Reduces expensive directory scans on network mounts. Set to 0 to disable caching. |
300 |
SUCCESS_MESSAGE |
Optional message displayed in Tinfoil when the shop is loaded. Great for MOTD or custom greetings. | (empty) |
LOG_FORMAT |
Morgan-style log format: tiny, short, dev, common, or combined. |
dev |
You can protect all endpoints with HTTP Basic Auth by setting either AUTH_USER + AUTH_PASS or a single AUTH_CREDENTIALS value (user:pass). If none are set, authentication is disabled.
- Variables:
AUTH_USERandAUTH_PASS: username/password pairAUTH_CREDENTIALS: combineduser:passstring
- Precedence:
AUTH_USER/AUTH_PASStake priority overAUTH_CREDENTIALSwhen both are present.
Examples:
# Local (Bun)
AUTH_CREDENTIALS=admin:secret bun run src/server.ts
# Test without auth header (should be 401)
curl -i http://localhost:3000/
# Test with auth header (should be 200)
curl -i -H "Authorization: Basic $(printf 'admin:secret' | base64)" http://localhost:3000/Docker Compose:
services:
tinfoil-bolt:
env_file: .env
environment:
- PORT=3000
- GAMES_DIRS=/games/1,/games/2
- AUTH_CREDENTIALS=${AUTH_CREDENTIALS}Note: Keep the server on a trusted LAN. If your client supports Basic Auth, set credentials accordingly. If not, leave auth disabled.
- The root endpoint (
/or/tinfoil) returns an index listingshop.jsonandshop.tfl - Tinfoil requests
/shop.tflwhich contains the actual game library with relative URLs - Each configured directory is aliased (e.g.,
games,games-2) to keep paths unique across multiple mounts - Files are served from
/files/{alias}/{relative-path}
tinfoil-bolt supports HTTP 206 Partial Content responses, enabling resumable downloads for large files. This is especially useful for:
- Interrupted downloads: Resume a failed transfer without re-downloading the entire file
- Bandwidth efficiency: Download only the portion of a file you need
- Large files on unreliable connections: Split downloads across multiple requests
Supported Range Formats:
Range: bytes=0-1048575- First 1MBRange: bytes=500-- From byte 500 to end of fileRange: bytes=-1048576- Last 1MB (suffix range)
Server Response:
200 OK- Full file requested (no Range header)206 Partial Content- Valid range request accepted- Includes
Content-Rangeheader (e.g.,bytes 0-1048575/5242880) - Includes
Content-Lengthfor the requested range size
- Includes
416 Range Not Satisfiable- Invalid range (e.g., start >= file size)- Includes
Content-Range: bytes */{total_size}for client reference
- Includes
All file responses include the Accept-Ranges: bytes header to advertise support.
Example:
# Resume a partially downloaded 5GB file, starting from byte 2147483648
curl -H "Range: bytes=2147483648-" http://localhost:3000/files/games/large-game.nsp -o game.nsp --continue-at -
# Download first 10MB
curl -H "Range: bytes=0-10485759" http://localhost:3000/files/games/game.nsp -o game-chunk1.nsp
# Download last 512MB
curl -H "Range: bytes=-536870912" http://localhost:3000/files/games/game.nsp -o game-final-chunk.nsp- Open Tinfoil on your Nintendo Switch.
- Navigate to File Browser.
- Press - (Minus Button) to add a new location.
- Fill in the details:
- Protocol:
HTTP - Host: Your Server IP (e.g.,
192.168.1.50) - Port:
3000(or whatever you set) - Path:
/(or/tinfoil- both work) - Title:
Bolt Server(or whatever you like) - Username: (if auth enabled, enter your
AUTH_USERvalue) - Password: (if auth enabled, enter your
AUTH_PASSvalue)
- Press X to Save.
Tinfoil will fetch the index, then request /shop.tfl to load your library. Your "New Games" tab will populate automatically.
Note: If you have enabled Basic Auth on the server, Tinfoil's File Browser will prompt for credentials when connecting. Leave username/password blank if auth is disabled.
The server recursively scans for the following extensions:
.nsp.nsz.xci.xciz
This application serves files over plain HTTP (required by Tinfoil's standard implementation). It is intended for use inside a trusted local network (LAN). Do not expose this port directly to the internet without a VPN or proper authentication layer.
MIT License. Feel free to fork, mod, and use in your home lab.
Disclaimer: This tool is for managing your legally owned backups and homebrews. The authors do not condone piracy.
