# Using Ownfoil Once Ownfoil is running, the Shop Web UI is accessible with your computer/server IP and port, by navigating to `http://:8465`, i.e. `http://localhost:8465` from the same computer or `http://192.168.1.100:8465` from a device in your network. # First steps 1. Open the Web UI. 2. Go to `Admin` → `Settings` and __create an admin user__. Until you do, authentication is disabled and anyone who can reach the Web UI can change the configuration of your shop. 3. Upload your [console keys](#console-keys) under `Titles`, so your files can be identified whatever their name. 4. Configure the [workers](#workers) to optimize identification for large libraries. 5. Add the directories containing your games under `Library` → `Paths`. Ownfoil scans a path as soon as you add it. 6. Open the `Setup` page and configure your client on your Nintendo Switch with the values it shows you. By default the organizer and compression features are disabled until you turn them on, so your library isn't modified unexpectedly. # Console keys Console keys are the keys dumped from your own Nintendo Switch. Ownfoil uses them to decrypt the metadata inside your files, which is how it identifies a game regardless of what the file is called. Upload your `prod.keys` (or any `.keys` / `.txt` file with the same content) in the `Settings` under `Titles`, then hit `Submit`. Ownfoil checks every master key revision it contains and tells you if one is missing or wrong. A missing revision means the games released after it cannot be decrypted, so they will not be identified. Without keys, Ownfoil falls back to reading the filename, and every file __must contain `[TITLEID][vVERSION]`__ or it won't be recognized. These features need valid keys and are unavailable without them: * identification of files that aren't named `[TITLEID][vVERSION]` * [metadata extraction](#title-metadata), for games not covered by titledb * [file verification](#file-verification), which is disabled when no valid keys are loaded * [file compression](#file-compression), which has to decrypt the content to recompress it # The Web UI There are three main pages: the library view, the setup guide, and under `Admin` the settings, the services, the library files, the task list and the stats. ## Library view The landing page, a grid view of your whole library, with labels and filtering to highlight owned and missing content. You can filter by type (base game or DLC), by ownership, by whether an update is missing, and by whether the DLC set is complete. There is also a search box, a card and an icon view, a card size slider and a page size selector. The version badge on a card opens a popover listing every known version of that title with its release date, and whether you own it. ## Setup page This is the page to use when configuring a client on your Nintendo Switch, with values already filled in with your own address and port, and the exact menu path to follow in each app. It leads with [Sphaira](#sphaira). Opened from your local network, it also tells you whether [discovery](#discovery) is enabled and, if so, how to let Sphaira find your server by itself. The server's local and remote addresses are given to add it manually otherwise. The [legacy clients](#legacy-clients) are in a collapsed section below. Its `Local Access` tab uses your server's LAN IP, and its `Remote Access` tab the `Shop URL` configured in the [Shop](#shop) settings. ## Files page Admin only, to manage library files; actions run as [tasks](#tasks-page). The tiles at the top count the files in each state, so the page shows at a glance whether anything needs attention. Clicking a tile lists its files, and says whether the related setting is enabled, with a link to it: - `All files`: every file in all libraries. - `Unidentified`: files Ownfoil could not match to any known content in the metadata database. - `Verification issues`: files whose signature or content hash [check](#file-verification) failed, and files not verified yet. - `Duplicates`: for each content with several copies, the copy kept and why it wins over the others. - `Outdated updates`: update files older than the newest update owned for the same title. Bundles are never deleted. - `Pending`: files with processing still due given the current settings. The search box matches a title's name or id, or a file name. `Filter` adds filters on the content type, the format, the verification status, how the file was identified, whether it is compressed or organized, and its library when you have several. Each filter shows as a chip: click it to change what it matches, or its cross to remove it. ## Tasks page Everything Ownfoil does in the background is a task, and this page shows them live: what is queued, what is running with its progress, what is scheduled to run later, and what failed. Failed tasks stay until you dismiss them, so you can see what went wrong earlier. The worker section shows what each worker process is doing right now. How many there are is configured in [Workers](#workers), you can use it to see if the backlog of tasks can be optimized. ## Services page Admin only, for configuring other services your Ownfoil server interfaces with. ### Discovery | Setting | Default | Description | | --- | --- | --- | | `Enabled` | enabled | Listen for and answer discovery requests from clients on the local network. | Discovery lets a client on your Switch find your shop by itself, instead of you typing its address on the console. The client broadcasts a request on your local network on UDP port `8465`, and Ownfoil answers with the shop name, the port to reach it on and the remote `Shop URL` if you configured one. It only works when your Switch and Ownfoil are on the same network, but the remote `Shop URL` is reported in the discovery response: in Sphaira it is then saved and used when away from home. > [!IMPORTANT] > With Docker, discovery only works if the container uses the host network: `--network host` with `docker run`, or `network_mode: host` with Docker compose. ## Settings page Admin only, and a single scrolling page. # Clients ## [Sphaira](https://github.com/NaGaa95/sphaira/releases) Sphaira has a built-in Ownfoil menu, under `Menu` → `Menus` → `Ownfoil`, and is the recommended client. - Finds local servers by itself with [discovery](#discovery) - A local and a remote address per server, over `HTTP` or `HTTPS`, used in that order - User authentication - Browsing by game rather than by file: `New games`, `Updates`, `DLC`, `All games` and `Search`, paginated and sortable - Pick a version and the DLC you want, only the content needed is downloaded, whichever files it is stored in - Compares against what the console has installed already - Compressed content (NSZ and XCZ) support - Downloads resume after a dropped connection - Artwork served by Ownfoil, the console never contacts Nintendo's servers - Custom welcome message (MOTD) Select `Discover local servers` and pick your shop, or add it manually with `X` using the local and remote addresses shown on the `Setup` page. ## Legacy clients These clients browse the shop from a file list using their own title metadatas, which can differ from what's actually identified by Ownfoil. They can be enabled and disabled individually in the [Shop](#client-access) settings. ### [Tinfoil:](https://tinfoil.io/Download) - `HTTP` / `HTTPS` protocol support - User authentication - Shop browsing with icons and banners - [Content filtering](#content-filters) (games, updates, DLC, multi-content) based on URL - New games, DLC, Updates, Recommended and XCI sections - Compressed content (NSZ and XCZ) support - Encrypted shop support - Client side Host verification for secure connections - Tinfoil shop customization ### [Sphaira file browser:](https://github.com/NaGaa95/sphaira) - `HTTP` / `HTTPS` protocol support - User authentication - Directory-based file browsing - [Content filtering](#content-filters) (games, updates, DLC, multi-content) based on URL - Compressed content (NSZ and XCZ) support Mounted as an `HTTP` location in its file browser, Sphaira browses your shop as a folder tree rather than a shop listing, so what you see is the layout of your library on disk. Opening a file shows a preview of its content, to actually install it press `Options` → `Install`. Sphaira identifies itself in its requests since version `1.0.6`, which Ownfoil needs to serve the shop. Be sure to use an up to date version if encountering issues. ### [CyberFoil:](https://github.com/luketanti/CyberFoil) - `HTTP` / `HTTPS` protocol support - User authentication - Shop browsing with icons and Sections (Updates, DLC) - Compressed content (NSZ and XCZ) support - Client side Host verification for secure connections - Custom welcome message (MOTD) > [!TIP] > Check the `Setup` page in the Web UI for specific instructions on configuring each app, using local or remote access. ## Content filters Adding a path to the shop URL configured in your client filters what it serves: | Path | What you get | | --- | --- | | `/` | Everything, including files Ownfoil could not identify | | `/base` | Games only | | `/update` | Updates only | | `/dlc` | DLC only | | `/multi` | Files containing more than one content, typically an `xci` bundling a game with its update and DLC | This is how you get several shops out of one Ownfoil: add one entry per filter in your client and you can browse your updates without scrolling past every game you own. Note that only the unfiltered shop shows unidentified files. If a file shows up in the root shop but in none of the filters, it was not identified. # Settings reference The sections below follow the order of the `Settings` page. ## Authentication Ownfoil requires an `admin` user to be created to enable authentication for your Shop. Create the first one here, it will have admin rights, then you can add more users to your shop the same way. Each user has up to three permissions: | Permission | Grants | | --- | --- | | `Shop` | Access to the shop from a client, to file downloads, and to the library page. | | `Admin` | Access to the `Settings` and `Tasks` pages and everything that changes the configuration. Admins get the other two permissions automatically. | | `Backup` | Reserved for a future feature, it currently grants nothing. | You can also create users from [environment variables](./Install.md#environment-variables) at startup, which is handy to avoid ever running an instance without an admin. ## Library This section is where you tell Ownfoil what your library is, how to watch it, and how to automatically manage the files in it. Every file goes through the same pipeline, in order: it is __identified__ (what game, which version, base/update/DLC it contains), its [metadata](#title-metadata) is __extracted__, it is __verified__, __organized__, then __compressed__. A step that is disabled or already done is skipped, so a settled library does nothing at all. This is why enabling compression on an existing library starts compressing everything. ### Paths Add the directories containing your content here. The path has to already exist on the machine running Ownfoil: with Docker that means the path *inside* the container, so `/games`, not the path on your NAS. Ownfoil scans a path as soon as you add it, looking for `nsp`, `nsz`, `xci` and `xcz` files. The arrows button on a row rescans that one path, and `Scan library` rescans all of them. If enabled, the file watcher below detects changes automatically. ### File watcher | Setting | Default | Description | | --- | --- | --- | | `Enable file watcher` | enabled | Watch your library paths for changes. | | `Polling interval` | `60` seconds | How often network paths are checked. | Files moved, renamed, added or removed are reflected directly in your library, without a scan. Local directories are watched through the operating system, which notifies Ownfoil the moment something changes and costs nothing while idle, the polling interval is not used for them at all. Network filesystems (NFS, SMB, a mapped Windows drive) cannot do that, so they are checked at the interval you set instead, which is why a change on a NAS takes up to one interval to show up. Lower it if you want your library to react faster, at the cost of walking the whole tree more often. ### Management Automated library management. Everything in this section shares the `Submit` button at the bottom. | Setting | Default | Description | | --- | --- | --- | | `Delete older updates` | disabled | Deletes older update files when a newer version of the same update is in your library. | | `Delete duplicate files` | disabled | When several files carry the same content, keeps the best copy and deletes the others. | | `Prefer multi-content files` | disabled | Keep files bundling several contents over single-content copies of them. Also applies to downloads. | Beside `Delete older updates` and `Delete duplicate files`, and beside `Verify files` under [File verification](#file-verification), the settings page shows how many files each would act on now. Clicking the count opens the [Files page](#files-page) on the matching list. The best copy is an intact file over a [corrupt or modified](#file-verification) one, then a single-content file over a multi-content one unless you prefer them, then a [compressed](#file-compression) and organized file, and finally the oldest. A multi-content file is only deleted once every content it holds has a better copy elsewhere, and nothing is judged until every copy of it has been through the pipeline. > [!TIP] > Deleting duplicates needs valid [console keys](#console-keys): a file identified only from its filename is never deleted. #### File compression | Setting | Default | Description | | --- | --- | --- | | `Compress files` | disabled | Compress files after they are added and organized. | | `Compression level` | `18` | zstandard level, `1` to `22`. Higher is smaller but slower. | Compression converts `nsp` to `nsz` and `xci` to `xcz`, which saves storage space depending on the game. Both formats install fine on all supported clients. The compressed file is written next to the original, then every piece of content inside it is decompressed again and hashed, and compared against the same content in the source file. The original is only deleted once every one of them matches. If anything fails the original is left exactly where it was and the incomplete output is removed. A file that [verification](#file-verification) found to be corrupt is not compressed. > [!TIP] > Compression needs valid [console keys](#console-keys).
Advanced compression options | Setting | Default | Description | | --- | --- | --- | | `Long-distance mode` | disabled | Better ratio on large files, at a higher memory use. | | `Compression mode` | `auto` | `solid` gives the best ratio, `block` allows random reads so a file can be streamed or mounted without decompressing it fully. `auto` uses solid for `nsp` and block for `xci`. | | `Block size exponent` | `20` | Block size is `2^x`, from `14` to `32`. `20` is 1 MB. Only used in block mode. | | `Threads per file` | `0` | Threads a single compression uses, `0` picks automatically. | `Threads per file` multiplies with the number of tasks running at once, so a high value combined with several [I/O tasks](#workers) will oversubscribe your CPU.
#### File verification | Setting | Default | Description | | --- | --- | --- | | `Verify files` | enabled | Check that your files are original and intact. | | Depth | `Full hash` | `Signature only` or `Full hash`. | One aspect of library curation is to know whether or not the files in your collection are valid. This means different things for different users: some want to ensure every file is original and intact, while for others a modified but working file is enough. File verification is a tedious chore, but a mandatory one to make sure your backups will install and work, or if they need to be replaced. `Signature only` checks that the file decrypts and that every content header is signed by Nintendo. It is near instant, and it catches a file that isn't what it claims to be. `Full hash` also reads every byte and compares it against the hash the file itself declares, which is the only way to catch actual corruption. After files have been been successfully decrypted and identified, automatic verification will run in a background task. Using `signature` and content `hash` validation, each file will be classified as either: - `valid`: original content from Nintendo, intact. - `repack`: content has been modified, but metadata has been updated to allow integrity verification and it matches. - `modified`: content has been modified but integrity metadata has not been updated. - `corrupt`: the integrity of the content cannot be verified. A `corrupt` file should be deleted or replaced, as installation will fail, but anything else should be fine to keep. Verification is disabled entirely, with the controls greyed out, when no valid [console keys](#console-keys) are loaded. #### Organizer | Setting | Default | Description | | --- | --- | --- | | `Enable organizer` | disabled | Move identified files into the paths built from the templates below. | | `Remove empty folders` | disabled | Remove folders left empty after files are moved out of them or deleted. | | `Windows compatible filenames` | disabled | Use filenames a Windows system can read. | Once a file is identified, the organizer renders the matching template, and moves the file there if it isn't already. Paths are relative to the library path the file is in, and the extension is added automatically. If another file already has that name, a `(2)` is appended to it, and removed once the other file is gone. By default, file names are sanitized based on the system running Ownfoil, for compatible characters. If Ownfoil is running on a Linux server but you are accessing your library on Windows (with an SMB or NFS mount), you can force the organizer to comply with file names that will render correctly in Windows. ##### Templates | Template | Default | | --- | --- | | `Base template` | `{titleName}/{titleName} [{appId}][v{appVersion}]` | | `Update template` | `{titleName}/{titleName} [{appId}][v{appVersion}]` | | `DLC template` | `{titleName}/{appName} [{appId}][v{appVersion}]` | | `Multi-content template` | `{titleName}/{titleName} [{titleId}]` | Available in every template: | Attribute | Description | | --- | --- | | `{extension}` | File extension, e.g. `nsp`, `nsz` | | `{titleId}` | Title ID of the game | | `{titleName}` | Name of the game title | Available in the base, update and DLC templates: | Attribute | Description | | --- | --- | | `{appId}` | Application ID | | `{appVersion}` | Application version | | `{patchLevel}` | Patch level | | `{appName}` | Name of the application, the title or the DLC name | > [!CAUTION] > Make sure the templates contain `[{appId}][v{appVersion}]` for Tinfoil to recognize the apps. ## Titles | Setting | Default | Description | | --- | --- | --- | | `Library Region` | `US` | Region used to get games informations. | | `Library Language` | `en` | Language used to get games informations. | This is the region and language of your shop, and for now it is the same for all users. It matches the names, descriptions and artwork for identified files. Changing it re-downloads titledb and, if the organizer is on, renames your files to match the new names. The available languages depend on the region you pick. `Console Keys file` is where you upload your keys - see [Console keys](#console-keys). Below it, `Master key revisions` reports what Ownfoil found in the file you uploaded, and names any revision that is missing or invalid. ### Title metadata Names, descriptions and artwork come from [blawar/titledb](https://github.com/blawar/titledb), which is updated upstream once per day but can remain without updates for long periods of time, and also miss games entirely. To work around content missing from `titledb` Ownfoil manages its own metadata database, merging `titledb` data with the metadata extracted directly from the files. This means homebrew ports and games missing from the uptream `titledb` will always be identified, organized, renamed and served with their metadatas. ## Local artworks | Setting | Default | Description | | --- | --- | --- | | `Download artwork locally` | enabled | Store the artwork of your library on your server. | For your shop to be fully self-hosted and not rely on external sources, Ownfoil can download all artworks locally and serve them to clients. Instead of providing direct eShop links, your Switch will retrieve them from your server and never connect to Nintendo's servers. This covers the icon, banner and screenshots of every game in your library and of its DLCs, owned or not. Each image is also resized to the dimensions clients display it at, so your Switch downloads a lighter image than the original. Artwork is fetched again after each titledb update, and changing the library language or region downloads the matching artwork. `Disk usage` shows how much space the artwork takes, by type, for the originals and the resized copies. Turning the setting off stops new downloads, the artwork already stored is still served. ## Shop | Setting | Default | Description | | --- | --- | --- | | `Shop name` | `Ownfoil` | Name of your shop, shown in the Web UI and in clients. | | `Shop URL` | empty | The hostname your shop is reachable at from the internet, e.g. `shop.domain.tld`. | | `Public shop` | disabled | Serve the shop to clients without authentication. | | `Message of the day` | `Welcome to your own shop!` | Message presented in clients after successfully loading your shop. | The shop name replaces `Ownfoil` in the navigation bar, the browser tab and the app name when you add the Web UI to your home screen. The `Setup` page also uses it to name the shop entries it tells you to create in each client, so set it before configuring your Switch. It can also be used by homebrew clients when browsing the shop The MOTD is shown by Sphaira's Ownfoil menu, Tinfoil and CyberFoil. The Sphaira file browser does not display it. Setting `Shop URL` lets Ownfoil tell the client which address the shop is supposed to be served from, and the client refuses to load it from anywhere else. That is what stops someone who got hold of your URL and credentials from rebroadcasting your shop as their own. It only works on secure requests: if your reverse proxy still answers on plain `http`, or doesn't send `X-Forwarded-Proto`, none of this applies and your shop is unprotected. See [Remote access and HTTPS](./Install.md#remote-access-and-https). ### Client access | Setting | Default | Description | | --- | --- | --- | | `Tinfoil` → `Enabled` | enabled | Allow Tinfoil to access the shop. | | `Tinfoil` → `Encrypt shop` | enabled | Serve the shop listing encrypted. | | `Sphaira` → `Enabled` | enabled | Allow the Sphaira file browser to access the shop. | | `CyberFoil` → `Enabled` | enabled | Allow CyberFoil to access the shop. | Disabling a client makes Ownfoil refuse it with a message. These only apply to the [legacy clients](#legacy-clients), Sphaira's Ownfoil menu is always allowed. `Encrypt shop` compresses and encrypts the shop listing in a way only a real Tinfoil build can read, so only a Tinfoil client will be able to inspect the shop's content. Note that if a client other than Tinfoil is enabled (and not encryption is available), the shop content will be served in clear through their headers, rendering encryption useless. ## Scheduler | Setting | Default | Description | | --- | --- | --- | | `TitleDB update interval` | `12h` | How often to refresh titledb and re-process your library. | The interval is a number followed by a unit: `s`, `m`, `h` or `d`, so `30m`, `2h` and `1d` are all valid. Set it to `0` to disable the periodic run entirely. TitleDB is updated upstream once per day, this scheduled task will sync your local reference and add the new titles, updates and DLCs. Libraries will be re-processed after the update, for example to identify files to titles that were not available before. ## Workers | Setting | Default | Description | | --- | --- | --- | | `Worker count` | `2` | Number of worker processes. | | `Max concurrent I/O tasks` | `1` | How many disk-heavy tasks run at once. | | `Max concurrent network tasks` | `1` | How many artwork downloads run at once. | Workers are the processes that run everything in the background. More of them means more tasks in parallel like identifying, scanning, compressing. The concurrent I/O setting ensures CPU/disk heavy tasks do not loose the benefit of parallelism. Compression, decompression and verification read and write multi-GB files, and running several of them at once can be detrimental. If the files are on the same hard drive, parallel tasks will make the heads seek back and forth and will make processing time significantly longer. So this limit caps them independently of the worker count, while light tasks keep flowing. Pick it based on where your library lives: * __Network share or a single hard drive: `1`__ - avoids seek thrashing, the safe default. * __SATA SSD: `2` to `3`__ - no seek penalty, so you are limited by CPU. * __NVMe with spare CPU cores: raise it towards your core count__ - each compression uses about 3 to 4 threads, so the CPU, not the disk, is the ceiling.