Nextendo Prelude with synthetic per-console PRODINFO (CAL0) generation + custom Atmosphere overlay. Upstream IPs, no private deployment data.
  • C 91.6%
  • C++ 6%
  • Makefile 1.4%
  • Python 1%
Find a file
kutaro 050a6cb59c
All checks were successful
build-bundle / bundle (push) Successful in 20m24s
feat: Nextendo news in the HOME menu (v3.4.19, build 79)
Ports the HOME-menu news feature from upstream 3.5.12, which the previous
commit had deliberately left out. Re-examined and that exclusion was wrong:
nextendo_news.c performs NO network access at all (no gethostbyname, no
socket, no ssl, no hardcoded host). It only manipulates local bcat system
save data. The news themselves arrive over the bcat-*-lp1.cdn.nintendo.net
hosts, which nextendo_hosts_build() already redirects to our own server --
so this is not a phone-home to nextendo.network.

Added:
  - source/nextendo_news.{c,h}
    news:a -> CreateNewsService(0) -> ClearStorage(40200) to flush the two
    bcat system saves (0x...90 data, 0x...91 index/history), then
    RequestAutoSubscription(40101) to re-subscribe the HOME menu. Falls back
    to mounting the saves directly if the service call fails. passphrase.bin
    is never touched -- the news decryption key derives from it.
  - romfs/.../exefs_patches/nextendo_bcat_signature/
    bcat signature patch (build-id 6D9772A7..., sysNAND 22.5.0)
  - nextendo_apply.c: purge on both mode switches (Nextendo mode only when the
    signature patch covers this console's bcat build-id, otherwise our news
    would not render and we would be left with an empty list)
  - main.c: SCREEN_NEWS_REBOOT -- on first launch after updating while staying
    in Nextendo mode the patch lands on the SD for the first time, so purge and
    ask for a reboot (the bcat patch only loads at boot). Needed sys/stat.h;
    access() in devkitPro newlib returns 0 even for absent paths.
  - lang.{c,h}: STR_NEWS_TITLE / STR_NEWS_CLEANED in all SIX languages
    (upstream has five -- German is ours and stays)

STILL NOT PORTED:
  - Account-link fallback (network_mitm v2, title 4200000000000666) -- excluded
    on request. Not shipped, and NEXTENDO_STALE_FILES keeps deleting it.
  - Upstream deletion of nextendo_prodinfo.c -- our synthetic PRODINFO stays.

Verified on the built NRO (18,042,304 bytes, NRO0 magic): all three news
symbols linked (nm shows T), the "news:a" service name present twice as a
movk immediate at both call sites (it is loaded into x1 as a u64, NOT stored
as a string -- a string grep for it always fails, confirmed by objdump), the
bcat signature .ips embedded, both news strings 6-language. All 8 game patch
directories and the new host strings from v3.4.18 still present; account_link
tree, mitm.lst and nextendo_account_link_install all absent.
2026-10-07 12:46:01 +02:00
.forgejo/workflows fix: Nintendo mode must blank, not synthesize (new patch 0006) 2026-09-16 17:26:17 +02:00
atmosphere-patches fix: Nintendo mode must blank, not synthesize (new patch 0006) 2026-09-16 17:26:17 +02:00
contrib/npshop-ssl-mitm Prelude v3.4.3 + synthetic PRODINFO + custom Atmosphere patches 2026-09-12 20:48:34 +02:00
romfs feat: Nextendo news in the HOME menu (v3.4.19, build 79) 2026-10-07 12:46:01 +02:00
scripts CI: build the custom Atmosphere and assemble a full SD bundle 2026-09-12 21:50:05 +02:00
source feat: Nextendo news in the HOME menu (v3.4.19, build 79) 2026-10-07 12:46:01 +02:00
.gitattributes Prelude v3.4.3 + synthetic PRODINFO + custom Atmosphere patches 2026-09-12 20:48:34 +02:00
.gitignore ssl_mitm: add the prebuilt exefs.nsp (gitignore excluded *.nsp) 2026-09-12 20:49:18 +02:00
icon.jpg Prelude v3.4.3 + synthetic PRODINFO + custom Atmosphere patches 2026-09-12 20:48:34 +02:00
LICENSE.md Prelude v3.4.3 + synthetic PRODINFO + custom Atmosphere patches 2026-09-12 20:48:34 +02:00
LISEZ-MOI.txt Prelude v3.4.3 + synthetic PRODINFO + custom Atmosphere patches 2026-09-12 20:48:34 +02:00
Makefile feat: Nextendo news in the HOME menu (v3.4.19, build 79) 2026-10-07 12:46:01 +02:00
README.md feat: bump v3.4.17 (build 77) — Metal Gear Solid: Peace Walker cert bypass 2026-09-19 15:48:47 +02:00
Switch nnAccount Private Server Setup.txt Prelude v3.4.3 + synthetic PRODINFO + custom Atmosphere patches 2026-09-12 20:48:34 +02:00

Prelude (Generated Serial)

Prelude switches a Nintendo Switch between the Nextendo Network and Nintendo's official servers.

This build consists of two halves that belong together:

  1. nextendo.nro — the homebrew you launch from the HBMenu. It writes the configuration (hosts, CA trust, game patches) and reboots.
  2. Custom Atmosphère 1.11.2 — a patched ams_mitm that builds and injects a synthetic console identity at boot (the "generated serial").

The second half is what sets this build apart: without the patched Atmosphère the identity feature does nothing at all. Both halves are built together by CI and shipped as one ready-to-install SD bundle.

        Which network do you want to load?
        [ NEXTENDO ]     [ NINTENDO ]
  • Nextendo mode — redirects Nintendo traffic to the Nextendo servers and provisions everything the console needs to trust them.
  • Nintendo mode — removes every component Prelude installed and hands the console back to Nintendo, with Atmosphère's own telemetry blocking restored.

Quick start

What you need: a Switch that can boot CFW (RCM/modchip), an SD card, and the latest prelude-bundle-<tag>.zip from the releases page.

⚠ System firmware 22.5.0 is required

This bundle ships custom Atmosphère 1.11.2, whose minimum supported firmware is 22.5.0. On an older system version, CFW will not boot cleanly — you need to be on 22.5.0 (or newer) before installing. Update the console through Nintendo's normal system update first if you are below it. Prelude itself does not change your firmware; it just cannot run below that version.

Prelude detects on its own whether it is running on emuMMC or sysNAND, so you never pick that yourself. But what it can protect differs, so follow the section that matches your setup:

Installation (same for both)

  1. Back up your SD card. At minimum copy your existing /atmosphere/ folder somewhere safe — this bundle replaces it.
  2. Power off the console and put the SD card in your PC.
  3. Extract the bundle onto the root of the SD card, overwriting when asked. You should end up with /atmosphere/, /switch/ and fusee.bin on the card.
  4. Put the SD card back and boot into CFW using the fusee.bin from this bundle.
  5. Open the homebrew menu (hold R while launching a game, or use the Album).
  6. Launch nextendo.

⚠ You must use the new fusee.bin

Prelude is not just the .nro — the identity handling lives in the payload. The exosphere.ini keys that drive it (blank_prodinfo_*, nextendo_synth_prodinfo) are parsed by fusee, before Horizon even starts. An older payload does not know the synthesis key and silently ignores it.

If you keep booting your previous payload, the app will look like it worked — it writes the configuration and reboots — but the console comes up with the wrong identity: no synthetic serial in Nextendo mode, and on emuMMC no blanking in Nintendo mode either.

So make sure that:

  • your injector (TegraRcmGUI, NXLoader, Rekado…) points at this bundle's fusee.bin;
  • your modchip (Picofly/HWFLY/SX Core) has this fusee.bin as its payload on the SD;
  • /atmosphere/reboot_payload.bin comes from this bundle too — it is a copy of the same payload and is what "reboot to payload" uses. Extracting the bundle handles this for you;
  • if you boot through hekate, put fusee.bin in bootloader/payloads/ on the SD card (e.g. bootloader/payloads/fusee.bin) — it then shows up in hekate's Payloads menu. For autoboot, add a boot entry with payload=bootloader/payloads/fusee.bin to bootloader/hekate_ipl.ini.

Same goes for updates: a new Prelude release ships a new fusee.bin. Replacing only the .nro is not enough whenever the payload changed.

First launch takes a moment — the app provisions the whole certificate and patch tree onto the SD card the first time you apply a mode.


Boot into your emuMMC and run Prelude there.

  1. Pick NEXTENDO and confirm. The app writes the configuration and reboots.
  2. After the reboot, go online as usual — your console now talks to the Nextendo servers.

That is all. Your emuMMC boots with a synthetic console identity: a randomized serial number and device id, rebuilt from your real calibration data at boot. Nothing has to be enabled for this, it is the default on emuMMC.

To go back: launch Prelude again and pick NINTENDO. Your emuMMC then boots with a blank identity — not the synthetic one, and not your real one. That is the safe state for touching Nintendo's own servers, and it is what upstream Atmosphère does too.

Your sysNAND is never touched by any of this. It keeps its real identity, its eShop account and its online membership, exactly as before.

Serial the console reports
emuMMC, Nextendo mode synthetic (randomized)
emuMMC, Nintendo mode blank
sysNAND real — untouched

Quick start — sysNAND only

If your console has no emuMMC, CFW runs on the internal memory — the same NAND that carries your real eShop account and online membership. Prelude works fine here, but it deliberately does not alter that NAND's identity by default.

  1. Pick NEXTENDO and confirm. The app writes the configuration and reboots.
  2. After the reboot, go online as usual — your console now talks to the Nextendo servers.

You will see a warning card on the confirmation screen telling you this console has no emuMMC. That is expected, and it is not an error: it means no identity protection is active.

Is that a problem? For playing on Nextendo, no. Prelude redirects Nintendo's domains to the Nextendo servers, so Nintendo never receives the connection in the first place — your identity stays confined to your own server regardless of what the console would report.

To go back: pick NINTENDO. Your console is then a completely normal CFW console talking to Nintendo with its real identity, exactly as it was before you installed Prelude.

Optional: synthetic identity on sysNAND

If you want the randomized serial on sysNAND too, press X on the confirmation screen to toggle it. The warning card then shows the option as ON.

Understand what this does and does not buy you before enabling it:

  • It randomizes the serial number and device id only. All cryptographic material — most importantly the SSL client certificate — stays byte-identical, because changing it would break TLS entirely.
  • That certificate, not the serial, is what actually identifies your console to Nintendo. So the protection gained is largely cosmetic.
  • Meanwhile the risk is real: this manipulates calibration handling on the NAND holding your actual account.

The option applies to Nextendo mode only. Nintendo mode always hands your real identity back before any contact with Nintendo, whether the option is on or off.

Recommendation: leave it off on a console you care about. Use it on a test console, or set up an emuMMC instead — that is what emuMMC is for.

Serial the console reports
sysNAND, Nextendo mode, option off (default) real
sysNAND, Nextendo mode, option on synthetic (randomized)
sysNAND, Nintendo mode real — always, option ignored

If something goes wrong

Symptom What to do
Console does not boot / black screen after installing You overwrote a working /atmosphere/ with a different version (or your system firmware is below 22.5.0, which Atmosphère 1.11.2 requires). Restore your backup, and make sure the console is on 22.5.0 or newer before reinstalling.
Applied Nextendo mode, but the serial did not change You are almost certainly still booting an old payload. Point your injector or modchip at this bundle's fusee.bin — it is what parses the identity keys.
Nintendo mode still shows a made-up serial Same cause: an old fusee.bin does not know nextendo_synth_prodinfo and cannot turn the synthesis off.
Online games show 2618-201 NAT traversal failed. See the nncs1/nncs2 note under Servers.
"This console has no emuMMC" warning Expected on a sysNAND-only console. See Quick start — sysNAND only.
Want the stock console back entirely Pick NINTENDO mode, then restore your original /atmosphere/ folder from the backup.

Components — what does what

1. nextendo.nro (homebrew, source/)

Writes only files that Atmosphère already reads at boot. It does not patch games at runtime and does not touch the network stack.

What Where
Host redirections /atmosphere/hosts/{sysmmc,emummc}.txt (Atmosphère DNS.mitm)
DNS.mitm on/off /atmosphere/config/system_settings.ini
PRODINFO per mode /exosphere.ini (blank_prodinfo_emummc, emuMMC only)
Certificate trust exefs_patches/, nro_patches/, browser CA bundle, rootCA.pem
Game content (BCAT, mods, flags) /atmosphere/contents/ and exefs_patches/

The complete SD layout is embedded in the NRO (romfs/sd/) and provisioned to the card on first apply via copyTreeRomfs("romfs:/sd", "sdmc:"). That is why the .nro alone is a sufficient deliverable — a separate SD zip is not required.

Key source files:

  • source/nextendo_config.h — the server addresses (see the table below). Overridable at build time via -DNEXTENDO_SERVER_HOST=....
  • source/nextendo_apply.c — the apply flow for both modes and nextendo_hosts_build(), which generates the actual DNS.mitm hosts table written to the SD card. (source/nextendo_hosts.h only declares that function; it is not a host list.)
  • source/nextendo_prodinfo.c — stash/restore of the synthetic identity across mode switches.
  • source/nextendo_update.c — self-updater (Forgejo releases API, see Updating below).
  • source/nextendo_net.c — HTTPS client plus resolve_host(), which statically overrides hosts the Switch cannot resolve through Nintendo's own DNS.

Servers

Prelude does not point everything at a single machine:

Constant IP What goes there
NEXTENDO_SERVER_IP_DEFAULT 51.178.29.194 Account/BAAS/dauth/aauth, *.nintendo.com, *.srv.nintendo.net, the g2* NEX wildcard and most per-game hosts
NEXTENDO_SERVER_IP_ALT 3.135.232.168 Alternate server, selectable at runtime (see below)
NEXTENDO_SERVER_IP_NNCSD2 164.132.111.120 Mario Kart 8 Deluxe (g2b309e01-lp1.s.n.srv.nintendo.net) and the nncs2 NAT probe (nncs2-*.n.n.srv.nintendo.net)

The nncs2 split is not cosmetic: Pia requires two distinct IPs for nncs1/nncs2. If they are identical it deduplicates them, never sends the second probe, NAT traversal never completes and MK8 / Splatoon 2 fail with 2618-201.

Atmosphère's rule is last matching line wins, which is why the explicit MK8 line must come after the g2* wildcard.

A hidden D-Pad server switch on the picker screen (Up, Down, Left, Right) toggles between the default and alternate IP at runtime; the UI shows which one is active.

Some hosts are deliberately not redirected and resolve to the real Nintendo: d4c (system updates), ctest (browser connection test) and *.nintendowifi.net — otherwise the console shows error popups.

2. Custom Atmosphère — atmosphere-patches/ (6 patches on upstream 1.11.2)

Applied to the verified upstream tag 1.11.2 (commit 5388824be146a89619e8d641acd64599cf1c5f62). CI fetches the tag fresh from GitHub and checks the commit hash — nothing is built from a local tree.

Patch What it does
0001 Base: PRODINFO overlay in amsmitm_prodinfo_utils.cpp + SetOverrideRegisterInternalPki in libstratosphere (the hooks ssl_mitm links against)
0002 Loads nextendo_prodinfo.bin lazily — the SD is not mounted yet that early in boot
0003 Moves the overlay off the CAL0 read path; fixes the re-entrancy deadlock / black screen
0004 Synthesizes nextendo_prodinfo.bin from the real CAL0
0005 Runs the synthesis in InitializeProdInfoManagement(), not on the init thread
0006 Separates blanking from synthesis via a dedicated exosphere flag

Why blanking and synthesis had to be separated (patch 0006): up to 0005 the synthesis was gated on should_blank alone, which conflates two different things. Upstream, blanking is the protection in Nintendo mode — a blank identity cannot be tied back to the real console. Synthesizing there would instead show real Nintendo an invented serial whose device-certificate signature no longer matches: strictly worse than blank, and a state no legitimate console can be in. Parking nextendo_prodinfo.bin does not prevent it either, because the image is rebuilt from RAM on every boot and InitializeProdInfoManagement() runs before the SD card is mounted, so no file on the card can act as the gate.

Patch 0006 therefore adds a dedicated exosphere config item (65013, secmon flag bit 9), wired through the same path as ExosphereAllowCalWrites, and an exosphere.ini key nextendo_synth_prodinfo. The two settings are now independent:

blank_prodinfo_* nextendo_synth_prodinfo Result
1 0 blank identity (Nintendo mode — upstream behaviour)
1 1 synthetic serial (Nextendo mode)

Unlike the blank_prodinfo_sysmmc / blank_prodinfo_emummc pair, the synthesis key is deliberately not gated on the boot type: the blank keys already decide which boot type blanks at all, so one key covers both and Prelude never has to detect whether it is running on emuMMC before writing exosphere.ini.

Why the synthesis lives in Atmosphère and not in the homebrew: once blank_prodinfo is active, every CAL0 read from outside goes through this very MITM. Prelude as homebrew only ever sees the blanked image, and anything built from it fails IsValid(). ams_mitm does not mitm itself: g_calibration_info holds the genuine calibration data. That makes it the only place in the system where the real CAL0 is reliably available while blanking is on.

What the synthesis actually changes:

  • Randomizes the serial number (XKW + 11 alphanumeric characters)
  • Randomizes the device id embedded at offset 0xC6 of the ECC device certificate
  • Leaves all crypto material untouched — SSL key, SSL certificate, extended SSL key, amiibo roots. Otherwise TLS breaks.
  • Recomputes the two block CRC16s, the body SHA256 (over header.body_size) and the header CRC, then validates with IsValid() before writing.

The result is written to sdmc:/atmosphere/nextendo_prodinfo.bin so the identity stays stable across reboots, and is applied to the live blank buffer so no extra reboot is needed.

Timing (patch 0005): fs_mitm starts serving the CAL0 partition as soon as LaunchAllModules() runs, and HorizonOS reads the serial number very early. Swapping it on the init thread was too late — HorizonOS had already cached the blanked value. So the work is split: BuildNextendoProdInfo() touches no filesystem (source is RAM, randomness from os::GenerateRandomBytes, CRC/SHA are pure computation) and runs early; only PersistNextendoProdInfo() needs the SD and stays on the init thread.

3. ssl_mitm — contrib/npshop-ssl-mitm/

Sysmodule (title ID 4200000000000053), embedded as a prebuilt exefs.nsp. It links against its own copy of Atmosphere-libs (contrib/npshop-ssl-mitm/src/Atmosphere-libs) — patching the main Atmosphère does not patch that copy. It is not built in CI because its Makefile references a local symlink; the prebuilt NSP is committed.


Honest assessment: what the generated serial does and does not achieve

Rewriting the device id inside the ECC/RSA certificates only updates the CRC16, an integrity checksum — not a signature. The certificates are signed by Nintendo's CA, so a modified id yields an invalid signature.

  • Against your own server: perfectly fine, it does not verify them.
  • Against real Nintendo servers: not a plausible identity. A fully synthetic identity would require re-signing the certificates — out of scope.

Equally important: the serial number is not the identifier. The SSL client certificate is Nintendo-signed, unique per console, stays byte-identical in the synthetic file (it must, or TLS breaks), and goes out on every client-authenticated handshake. The real protection in Nextendo mode comes from the host redirection — Nintendo never receives the handshake at all.

Scope: by default blank_prodinfo_emummc affects emuMMC boots only, so the synthetic identity exists only there and sysNAND always keeps its real PRODINFO. On a console without an emuMMC this can be enabled explicitly — see below.

Optional: synthetic identity on sysNAND

fusee accepts both blank_prodinfo_sysmmc and blank_prodinfo_emummc, each gated on the boot type, so a console only ever reads the key that matches it. Prelude writes only the emuMMC key by default; the sysNAND key is an opt-in.

On the confirmation screen of a console without an emuMMC, press X to toggle it. The setting is stored in sdmc:/switch/prelude_sysnand.cfg and is off unless you turn it on. Turning it back off rewrites the key to 0, so a previously enabled console is not left blanking silently.

The option only ever applies in Nextendo mode. Nintendo mode always writes blank_prodinfo_sysmmc=0 and nextendo_synth_prodinfo=0, so the console is handed back untouched before you talk to Nintendo. See patch 0006 above for why the synthesis needs its own flag: parking the identity file is not enough to stop it.

Read this before enabling it. sysNAND is the NAND that carries your real eShop account and online membership. The synthesis does not blank anything — it serves a copy of the real CAL0 with a randomized serial and device id, leaving every byte of crypto material intact. But that is exactly the limit: the unchanged SSL client certificate is what actually identifies the console, so the gain is largely cosmetic while the risk (manipulating CAL0 handling on the NAND holding your real account) is not. Use it on a test console.

What if you only use sysNAND?

That is the normal case for a console without an emuMMC, and it works — the option above is not required for anything.

With the option off (the default), Prelude never touches blank_prodinfo_sysmmc, so the console keeps its real PRODINFO in both modes. In Nextendo mode that is not a leak: the DNS redirection means Nintendo never receives the handshake in the first place, so the identity stays confined to your own server. In Nintendo mode you are simply a normal CFW console talking to Nintendo with its real identity — which is what the warning card on the confirmation screen tells you plainly.


Reference

What is in the bundle

prelude-bundle-<tag>.zip (install steps are in Quick start above):

atmosphere/                  Custom Atmosphère 1.11.2 with the Nextendo patches (requires system firmware 22.5.0+)
atmosphere/package3          contains the patched ams_mitm as a KIP
atmosphere/reboot_payload.bin  copy of fusee.bin — used by "reboot to payload"
switch/nextendo.nro          Prelude
switch/daybreak.nro          (Atmosphère standard)
switch/haze.nro
switch/reboot_to_payload.nro
fusee.bin                    the payload you must boot — parses the identity keys
README.txt                   install instructions + content manifest

Three parts have to match. fusee.bin parses exosphere.ini (including nextendo_synth_prodinfo) and sets the secure-monitor flags; package3 carries the patched ams_mitm that acts on them; nextendo.nro writes the configuration. Mixing a new .nro with an old payload leaves the identity handling inert — see the warning in Quick start.

Mode switching and the synthetic identity

When switching to NINTENDO, nextendo_prodinfo.bin is parked in sdmc:/switch/prelude/ rather than deleted. Reason: generation draws a fresh serial and device id on every call, so each mode switch would mint a different synthetic console. A serial that changes on every toggle is itself a signal, and any server-side pairing keyed to the device id would break each time.

Updating

The NRO checks for a new release at launch and can download and replace the .nro it is running from (any SD path, not just /switch/). The update source is the Forgejo releases API at git.it-geek.at, not GitHub:

/api/v1/repos/KutaroKazuki/Prelude-Generated-Serial/releases/latest

The self-updater only replaces the .nro. It cannot touch fusee.bin, package3 or your injector, because those are not running when it updates — and on a modchip the payload may not even live on the SD card. When a release changes the payload or the Atmosphère patches (as v3.4.15 did), update by extracting the full prelude-bundle-<tag>.zip and re-pointing your injector at the new fusee.bin. Otherwise you get a new app on top of an old payload, which is the one combination where the identity handling silently does nothing.

Because the Switch resolves names through Nintendo's DNS and cannot look up that host, resolve_host() in source/nextendo_net.c hardcodes the connect target. SNI still uses the real hostname, so the TLS certificate validates normally.

Version numbers are kept in lockstep: APP_VERSION in the Makefile is X.Y.Z, the update checker compares full semver, and the release tag is vX.Y.Z. CI enforces this.

Manual rollback

  • Delete /atmosphere/nextendo_prodinfo.bin → ams_mitm falls back to a blanked PRODINFO.
  • Restore stock package3 → removes the overlay entirely.

Running your own server

The server IPs are defined in source/nextendo_config.h (NEXTENDO_SERVER_IP_DEFAULT, NEXTENDO_SERVER_IP_ALT, NEXTENDO_SERVER_IP_NNCSD2), or override at build time with -DNEXTENDO_SERVER_HOST="your.ip". The host-to-server mapping itself is generated by nextendo_hosts_build() in source/nextendo_apply.c — that is where you change which domain goes to which machine.

Keep in mind that nncs1 and nncs2 must resolve to two different IPs, otherwise Pia deduplicates the NAT probes and MK8 / Splatoon 2 fail with 2618-201.


Features

A complete settings app

  • Follows the console's system theme (HOME menu / Settings look, including light/dark mode).
  • In-app language selection: English, Español, Português, Français, 中文.
  • Background music, decoded with mpg123 — no SDL2, the .nro is ~35% smaller than before.
  • Self-updating (see Updating above). Stale bundled mods are detected and offered as an update too.

Online games, out of the box

One switch to Nextendo mode and these are configured with the proper hosts and patches:

  • Mario Kart 8 Deluxe — NEX secure-server routed to the production machine, plus a country flag installer (110 flags) that builds the ExeFS patch locally, no download needed.
  • Super Smash Bros. Ultimate — optional SSBU Online Deluxe quickplay mod (L button), bundled in the .nro with its full dependency stack (ARCropolis, smashline, Skyline plugins) and a toggle for the mod's own overclock if you already run sys-clk / Horizon OC.
  • Splatoon 2 — BCAT online schedule installer via LayeredFS (no save-data tricks, no network needed at install time).
  • Splatoon 3 — NPLN (gRPC/HTTP2) hosts plus the certificate ExeFS patches required by the game's bundled BoringSSL, with a status screen showing whether the installed patches match the ones this build ships and what to do after a game update.
  • Super Mario Bros. 35 — official BCAT event installer plus an optional Special Battle (27 merged events, 119 queues), installed on demand because its ExeFS patch replaces the normal 35-player battle instead of adding to it.
  • Super Mario Bros. Wonder — ExeFS patches (certificate + peer name) shipped for everyone; harmless if you don't own the game (Atmosphère matches by build ID).
  • Super Mario Party Jamboree — certificate ExeFS patch for the game's own bundled TLS stack. Jamboree speaks NPLN (gRPC over HTTP/2) rather than NEX, and our wildcard certificate already covers its tenant, so no peer-name patch is needed. Taken from upstream; shipped for everyone and likewise harmless without the game.
  • Metal Gear Solid: Peace Walker (Master Collection) — certificate ExeFS patch, same one-instruction pattern. Recovered from an upstream release that shipped only a built .nro with no matching commit, so it was extracted from that binary's RomFS and verified byte for byte rather than cherry-picked.
  • Explicit per-game NEX entries for Mario Tennis Aces, ARMS, Luigi's Mansion 3, Animal Crossing and Strikers, on top of the g2* wildcard.

Privacy & reversibility

  • Telemetry blocked in both modes (Atmosphère's native defaults are merged, not replaced).
  • Nintendo mode removes the whole certificate-trust stack (CA bundle, disable_ca_verification, rootCA.pem) and purges the logs that could leak the server IP — no .bak, no debug logs.
  • Your own hosts files are offered a one-time backup before Prelude writes over them.

Building

NRO locally

docker run --rm -v "$PWD:/work" -w /work devkitpro/devkita64 \
  bash -c 'dkp-pacman -Syu --noconfirm switch-freetype switch-mpg123 && make -j$(nproc)'

Produces nextendo.nro. The package names are switch-freetype and switch-mpg123 — no 2 suffix.

Never pass CFLAGS= on the make command line — CLI variables clobber the Makefile's own CFLAGS (the FreeType include path is lost). Edit source/nextendo_config.h instead.

Both halves via CI

.forgejo/workflows/build-bundle.yml builds both halves and assembles the bundle. It runs on pushing a v* tag or via workflow_dispatch. Steps:

  1. Version guard — APP_VERSION in the Makefile must match the tag.
  2. Build the NRO.
  3. Fetch the Atmosphère tag — clone tag 1.11.2, then verify both the commit hash and the version header (ams_api_version.h). A moved tag would otherwise silently build a different base.
  4. Apply the patches — git am atmosphere-patches/*.patch, then verify that nextendo_prodinfo.bin and SetOverrideRegisterInternalPki actually landed in the source.
  5. Build Atmosphère — Haze shaders serially first (-j1), otherwise the dksh target races; then make -f atmosphere.mk dist -j$(nproc) (dist lives in atmosphere.mk, not the root Makefile).
  6. Verify the binary — release NSOs are LZ4-compressed, so the debug ELF is grepped for nextendo_prodinfo.bin, BuildNextendoProdInfo, ApplyPendingNextendoProdInfoOverlay. It additionally verifies that the patched ams_mitm KIP is embedded byte-exact in package3.
  7. Verify the NRO — NRO0 magic, ssl_mitm embedded byte-exact, no foreign deployment data, and the expected server IPs present.
  8. Assemble the bundle (scripts/make_bundle.py) and release.

The AMS build takes a while (libstratosphere alone ~5 min, full build ~15 min) — hence a separate workflow, so the NRO goes green quickly on every push.

Not bit-reproducible: two builds of the same commit differ in ~60 of 17.9 M bytes, all inside GNU build-id notes. Never verify a CI artifact by SHA-comparing it against a local build.


PRODINFO and BIS key backups

automatic_backups/<SERIAL>_PRODINFO.bin and <SERIAL>_BISKEYS.bin are stock upstream Atmosphère, not a Nextendo feature — they are created on every CFW Switch on every boot. The code is byte-identical to upstream and there is no exfiltration (no socketInitialize / connect() / sendto anywhere in ams_mitm).

The risk is in handling, not in the code: these files identify the console, and the BIS keys decrypt its NAND. Never ship them in an SD image, backup folder, or zip. Note the irony after a Prelude apply: <REAL_SN>_PRODINFO.bin sits right next to the synthetic file — so the real serial is in a filename next to the anonymized one.


Special thanks

Special thanks to ESC and El Tඞnke for testing and for finding bugs.

In the early versions, sysNAND support simply was not working at all — it had been assumed to, but real hardware testing showed it never actually took effect. ESC and El Tඞnke were the ones who proved that out and kept the pressure on until it was fixed. They also caught the later case where Nintendo mode was synthesizing an invented serial with broken certificate signatures instead of handing back a clean blank identity — that one needed a new exosphere flag (patch 0006) and would have been a quiet, hard-to-spot failure on sysNAND. Their testing is the reason the identities in this release actually behave the way the docs say.

Licence

Copyright (C) 2026 Nextendo Network.

Prelude is licensed under the PolyForm Shield License 1.0.0. You may use, study, share and modify it for any purpose, and distribute your changes — with one exception: you may not use it to provide a product that competes with Nextendo Network, or with any product Nextendo Network provides using it.

See LICENSE.md for the full text, or https://polyformproject.org/licenses/shield/1.0.0.

Required Notice: Copyright 2026 Nextendo Network

This project is not affiliated with, endorsed by, or connected to Nintendo. "Nintendo" and "Nintendo Switch" are trademarks of Nintendo. Use it on hardware you own.