Skip to content

Lightweight alternative to the bloated tinfoil.io NUT server

Notifications You must be signed in to change notification settings

WorkaroundTech/tinfoil-bolt

Repository files navigation

tinfoil-bolt

tinfoil-bolt logo

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.

Why Bolt?

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_modules black 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/nas and /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 sendfile syscall (via Bun) for high-performance file streaming from your NAS to your Switch.

🛠️ Installation

Option 1: Docker Compose (Recommended)

This is the easiest way to run tinfoil-bolt on your NAS, Home Server, or Proxmox LXC container.

  1. Clone or download this entire repository.
  2. Copy .env.example to .env and update with your game directories and optional auth settings.
  3. Update the volume paths in docker-compose.yml to match your actual game storage locations.
  4. Run:
docker compose up -d

Option 2: Bare Metal (LXC/Linux)

If you are running a raw LXC container and don't want Docker overhead:

  1. Install Bun:
curl -fsSL https://bun.sh/install | bash
  1. Configure:
cp .env.example .env
# Edit .env with your game directories
  1. Run:
bun run src/server.ts
# or with npm-style scripts:
bun start

Configuration

Copy .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

Authentication (Optional)

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_USER and AUTH_PASS: username/password pair
    • AUTH_CREDENTIALS: combined user:pass string
  • Precedence: AUTH_USER/AUTH_PASS take priority over AUTH_CREDENTIALS when 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.

How It Works

  1. The root endpoint (/ or /tinfoil) returns an index listing shop.json and shop.tfl
  2. Tinfoil requests /shop.tfl which contains the actual game library with relative URLs
  3. Each configured directory is aliased (e.g., games, games-2) to keep paths unique across multiple mounts
  4. Files are served from /files/{alias}/{relative-path}

Advanced Features

HTTP Range Request Support

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 1MB
  • Range: bytes=500- - From byte 500 to end of file
  • Range: 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-Range header (e.g., bytes 0-1048575/5242880)
    • Includes Content-Length for the requested range size
  • 416 Range Not Satisfiable - Invalid range (e.g., start >= file size)
    • Includes Content-Range: bytes */{total_size} for client reference

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

Tinfoil Setup (Switch)

  1. Open Tinfoil on your Nintendo Switch.
  2. Navigate to File Browser.
  3. Press - (Minus Button) to add a new location.
  4. 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_USER value)
  • Password: (if auth enabled, enter your AUTH_PASS value)
  1. 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.

Supported Formats

The server recursively scans for the following extensions:

  • .nsp
  • .nsz
  • .xci
  • .xciz

Security Note

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.

License

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.

About

Lightweight alternative to the bloated tinfoil.io NUT server

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published