Repository navigation
Building from Source
This page takes you from a clean computer to Moonfin packages you can put on a Samsung or LG TV. You don't need Samsung's or LG's development kits to build. Windows, macOS and Linux all work.
For ready-made packages instead, see Installation and Sideloading.
| Tool | Version | Notes |
|---|---|---|
| Node.js | The current LTS release (22 or newer) | Comes with npm. The builds run on lts/* in CI |
| Git | Any recent version | To get the code |
| A terminal | Terminal on macOS and Linux, PowerShell on Windows |
That's the whole list. The two build tools the project needs, @enact/cli and @webos-tools/cli, install themselves in the next step.
- Windows: download the LTS installer from nodejs.org and run it with the defaults. Then install Git for Windows. Open a new PowerShell window afterwards.
-
macOS: with Homebrew,
brew install node git. Or use the installers from the same two sites. -
Linux: your distribution's
nodejs,npmandgitpackages, as long as Node is 22 or newer.nvmorfnmare the easy way to get a current LTS on older distributions.
Check it worked:
node --version
npm --version
git --versiongit clone https://github.com/Moonfin-Client/Smart-TV.git
cd Smart-TV
npm installThe repo uses npm workspaces (packages/*), so this one install covers every package. It also installs the two build CLIs into the git-ignored tools/ folder. That takes a minute or two the first time and needs internet.
# Samsung: all three variants in one command
npm run build:tizen:all
# Or one at a time
npm run build:tizen # Regular
npm run build:tizen:oblong # Oblong (1920x1080 launcher icon)
npm run build:tizen:legacy # Legacy (Tizen 2.4, no Smart Hub Preview)
# LG
npm run build:webos
# Fire TV (Vega OS), needs the Vega SDK on the PATH for the package step
npm run build:vega
# Xbox, on Windows with the Visual Studio Build Tools and their UWP workload
npm run build:xboxThe packages land in the project root:
- Samsung:
Moonfin_Tizen_<Regular|Oblong|Legacy>_v<version>.wgt - LG:
Moonfin_webOS_v<version>.ipk - Fire TV:
Moonfin_Vega_v<version>.vpkg - Xbox:
Moonfin_Xbox_v<version>.msix, with the dependencies a console needs underpackages/build-xbox/shell/Moonfin.Xbox/AppPackages/
They are built exactly the same way as the files on the Releases page. The Samsung package is unsigned, which is what Apps2Samsung expects. See Installation and Sideloading for which variant a TV needs.
The Developer Mode app on the TV has to be on with Key Server enabled (steps under Installation and Sideloading). Then, once per TV:
npx ares-setup-deviceAnswer the questions: name the device tv, give the TV's IP address, and paste the passphrase from the Developer Mode app. After that, every build goes on with:
cd packages/build-webos
npm run install-tv # installs build/*.ipk on the device named tv
npm run launch-tv # starts itnpx ares-install --device tv Moonfin_webOS_v<version>.ipk from the project root does the same by hand.
The simplest way is to feed the .wgt you built to Apps2Samsung, exactly as with a downloaded release. No Samsung tooling needed.
To install straight from the build script instead, you need Samsung's tooling and a certificate for your TV:
- Install the Tizen extension for VS Code (Tizen Studio is retired but an existing install works too). It brings the
tizenandsdbcommands and the Certificate Manager. - Turn on Developer Mode on the TV with your computer's IP (Apps screen, press 1 2 3 4 5), restart the TV, then connect:
sdb connect <tv ip>.sdb devicesshould list it. - Make a certificate with Certificate Manager while the TV is connected, so it can fill in the TV's device id (DUID). Sign in with a Samsung account when asked. Name the profile
Moonfinand it dropsauthor.p12anddistributor.p12into~/SamsungCertificate/Moonfin/, which is where the build script looks. - Build, sign, install and launch in one go:
cd packages/build-tizen
npm run install-tvTwo things that cost people time: tizen install -t wants the device name from the third column of sdb devices (something like UN32H5000FFXZA), not the ip:port from the first column. And retail TVs refuse most sdb shell commands, so the line successfully launched pid = N is the only confirmation you get from the tools. Look at the TV.
The web build is done by Enact like the others, then copied into the React Native shell under packages/build-vega/shell/ and packaged with Amazon's Vega SDK. The SDK only supports Ubuntu 20.04 to 24.04 and macOS, so on another distribution run it inside an Ubuntu container (distrobox works). Install it with curl -fsSL https://sdk-installer.vega.labcollab.net/get_vvm.sh | bash and source ~/vega/env. The virtual device cant run WebView apps on Linux, so a stick is the only way to test.
- On the stick: Settings > My Fire TV > About, press select seven times on the device name, then Developer options > Developer Mode. Run
vega devmode loginandvega devmode enable-device --code <the six digits>. Turning Developer Mode off again needs a factory reset. - Developer Options > Connection Mode > TCP/IP, then
vega exec vda connect <stick ip>:5555andexport VEGA_SERIAL=<stick ip>:5555. - Build, install and launch:
cd packages/build-vega
npm run launch-tvnode build.js --no-vpkg stops once the web build is in shell/assets/, which is all a machine without the SDK can do. Logs come from vega exec vda shell 'loggingctl log -v org.moonfin.androidtv -f', though only the shell's own console reaches it, not the page's. A Debug build (--debug) exposes Chrome DevTools on port 9229 through vega exec vda forward tcp:9229 tcp:9229, but Debug builds abort after about eight minutes on screen, so test playback on Release.
The web build is done by Enact like the others, then copied into the UWP host under packages/build-xbox/shell/Moonfin.Xbox/www/, which serves it to a WebView2 from the package, and the host is built into an .msix with MSBuild. That last step needs Windows with the Visual Studio Build Tools and their Universal Windows Platform workload. node build.js --no-msix stops once the web app is in place, which works on any system.
The package is signed with the certificate in MOONFIN_XBOX_PFX (password in MOONFIN_XBOX_PFX_PASSWORD), or with a throwaway one the script makes and keeps in .cert, which is all sideloading needs.
- Put the console in Developer Mode (the Xbox Dev Mode Activation app from the Store, with a Partner Center developer account) and turn on the Device Portal under Dev Home > Remote Access. Note the address it shows.
- Build, install and launch in one go:
cd packages/build-xbox
node build.js --debug --install <console ip>--debug makes a Debug build whose version counts up on every install, so it goes on over the copy already there and the sign in survives, and it exposes the page to Edge's remote DevTools through the Device Portal. node console.js <console ip> install|launch|close|press|screenshot drives a console by itself. --tester makes the Release package to hand to testers, --store the upload for the Microsoft Store, and --dev-url <url> a Debug package that loads the app from npm run dev:xbox instead of the package, so changes show on the console as they are saved.
npm run dev:tizen # serves packages/app with REACT_APP_PLATFORM=tizen
npm run dev:webos # serves packages/app with REACT_APP_PLATFORM=webos
npm run dev:vega # serves packages/app with REACT_APP_PLATFORM=vega
npm run dev:xbox # serves packages/app with REACT_APP_PLATFORM=xboxOpen the address it prints in a desktop browser. The platform is selected through REACT_APP_PLATFORM, so the dev server exercises the same platform detection the packaged app uses. See Development for how that abstraction works.
A browser dev server is subject to CORS restrictions that packaged .wgt and .ipk builds aren't, so some server requests behave differently there than on a TV. Focus behavior, performance and video don't fully reproduce on a desktop either. Test on real hardware before calling something done.
cd packages/app
npx enact lint
npm testThe Tizen build also runs the legacy CSS gate (node scripts/check-legacy-css.js from the root), which fails on CSS the 2016 sets can't render.
Advanced: where the build CLIs live, and updating them
@enact/cli (both platforms) and @webos-tools/cli (the webOS ares-* packaging tools) are installed in the git-ignored tools/ directory rather than as devDependencies. Both publish npm-shrinkwrap.json files that pin their entire dependency trees, including outdated packages that npm cannot override or audit fix from a consuming repo, so keeping them out of the repo's lockfile keeps those pins out of the dependency graph and its Dependabot alerts. Their commands (enact, ares-package, ...) still work in npm scripts and via npx through thin forwarders in packages/cli-shim. The root postinstall (scripts/ensure-clis.js) installs them.
The versions are pinned in tools/package.json, currently @enact/cli 7.2.1 and @webos-tools/cli 3.2.5. To upgrade, bump the pin, delete tools/node_modules, run npm install at the root, then verify the three things a new @enact/cli can break: the lint gate (enact lint in packages/app), the legacy patches (node scripts/patch-enact-legacy.js must report no failures), and a full build of each platform. @enact/cli is held at 7.2.1 because 7.3.x ships eslint-plugin-react-hooks 6, whose new rules flag about 55 errors in the app and fail both the lint gate and enact pack. Upgrading it means fixing those findings first (the legacy patches already apply cleanly on 7.3.3).
Never add these CLIs back to devDependencies, and keep tools/package-lock.json git-ignored.
Advanced: signing the Samsung package with your own certificate
Only needed if you want the build script itself to produce a signed package. It requires the Tizen CLI, a Samsung certificate created with the Certificate Manager, and a profile in profiles.xml.
npm run build:tizen -- --signedThe script looks for a profile named Moonfin and for author.p12 and distributor.p12 under ~/SamsungCertificate/Moonfin (%USERPROFILE% on Windows). If your setup differs, point it at the right places:
| Variable | Purpose |
|---|---|
TIZEN_SIGN_PROFILE |
Signing profile name (default Moonfin) |
TIZEN_CERT_DIR |
Folder holding author.p12 and distributor.p12
|
TIZEN_PROFILES_XML |
Full path to your tooling's profiles.xml
|
TIZEN_CLI |
Full path to tizen / tizen.bat
|
TIZEN_STUDIO_HOME |
Tizen tooling install root, if it is somewhere unusual |
Without all of certificate, profile and CLI present, the build falls back to an unsigned package and tells you what was missing. With valid certificates present, the plain npm run build:tizen signs by default. To force the unsigned path even when the Tizen CLI is installed:
npm run build:tizen -- --skip-tizen-cliMissing .pwd files referenced by profiles.xml do not break signing.
Advanced: the Homebrew Channel manifest
npm run build:webos also refreshes packages/build-webos/org.moonfin.webos.manifest.json with the new version and the SHA-256 of the .ipk. That file is uploaded with every release and is what the webOS Homebrew Channel catalog reads, so it has to be committed alongside a version bump.
ClassNotFoundException: org.tizen.ncli.ide.shell.Main, or a path containing \\..\..\..\sdk.info
The Tizen CLI works out where it is installed from the path it was started with, and looks for sdk.info relative to that. If it is started by name rather than by full path, it searches from the wrong folder and cannot load its own libraries. The build script always calls it by full path, so first make sure you are building from current main. If it still happens, your Tizen tooling is in a location the build script does not know about, and setting TIZEN_CLI to the full path of tizen.bat will fix it. You can also skip it entirely with --skip-tizen-cli, since the CLI is only used for signing and for installing straight to a TV.
Samsung certificate or profiles.xml not found
These are warnings, not errors. The build continues and produces an unsigned package, which is what you want for normal sideloading with Apps2Samsung.
There is no <x> target from tizen install
You passed the ip:port from sdb devices. Pass the device name from the third column instead.
ares-install says the device is unreachable
Key Server is off in the Developer Mode app, or the passphrase changed after the TV restarted. Run npx ares-setup-device again with the current passphrase.
Build fails on Windows
Older versions shelled out to zip, which does not exist on Windows. Packaging is now done in Node with no external tools, so make sure you are building from current main.
npm install fails inside tools/
Delete tools/node_modules and run npm install at the root again. The CLIs are re-fetched from npm.
Using Moonfin
Going deeper
For developers