PunktfunkPunktfunk
Your host

Plugins

Plugins bring your game launchers into the library, sync a ROM collection or Playnite library, and hand a USB device on the couch to the host.

Install plugins to fill the host's game library from your launchers and to add features such as USB passthrough. The host scans no launcher on its own: install one plugin per launcher you use, and its titles appear on every client. SteamGridDB, Libretro and IGDB fill missing covers and details for all of them (Art & Metadata).

PluginHostWhat it does
SteamLinux, WindowsInstalled games and non-Steam shortcuts with Steam's art, plus Big Picture and desktop tiles.
LutrisLinuxYour Lutris games with art, and a tile that opens Lutris.
HeroicLinuxEverything Heroic manages (Epic, GOG, Amazon), and a tile that opens Heroic.
Desktop entriesLinuxEvery Game-category menu entry, Flatpak included, that no other plugin lists.
BottlesLinuxThe programs in each Bottle, launched through bottles-cli.
Epic Games StoreWindowsInstalled Epic titles.
GOGWindowsInstalled GOG games, launched directly without Galaxy.
PlayniteWindowsYour whole Playnite library, launched through Playnite (below).
Ubisoft ConnectWindowsInstalled Ubisoft titles, launched through Ubisoft Connect.
Amazon GamesWindowsInstalled Amazon Games titles, launched through the Amazon Games app.
Battle.netWindowsInstalled Blizzard titles, launched through Battle.net, which must be running.
EA appWindowsGames the EA app installed, launched through the EA app.
Rockstar GamesWindowsGames the Rockstar Games Launcher installed, launched through the launcher.
Game slotsLinux, WindowsKeeps a copy of a game's config or saves for each device and swaps it in at launch (below).
RTSS Frame CapWindowsCaps the game at the client's refresh rate through a RivaTuner Statistics Server you install (below).
itch.ioLinux, WindowsGames installed by the itch app, with covers.
WaydroidLinuxThe Android apps installed in Waydroid.
ROM ManagerLinux, WindowsYour ROM folders and RomM servers, each game in the host's best emulator for it (below).
RomMLinux, WindowsThe games on a RomM server, played with RetroArch; each downloads on its first launch.
SteamGridDBLinux, WindowsCovers, heroes and logos for every game in the library. Needs a free API key.
LibretroLinux, WindowsBox art, developer, publisher, year and genres for ROM Manager's games.
IGDBLinux, WindowsDescription, developer, publisher, year, genres and player count for every game. Needs a free Twitch app.
Play historyLinux, WindowsHow long each game was played and on which device, on the game's page.
Stats black boxLinux, WindowsRecords the streaming stats of every stream and keeps the last twenty, under Diagnostics → Performance.
VirtualHereLinux, WindowsHands a USB device on the couch to the host while you play (below).

Install from the console

  1. Open Plugins → Installed and check the Plugin runner card. Every plugin runs inside this service.
    • Not installed: install the runner (Troubleshooting).
    • Disabled: press Enable runner. The runner is on by default except on Arch and the Steam Deck on-device build.
    • Stopped: it is enabled but not running; see Troubleshooting.
  2. Browse → pick a plugin → Install. The host installs it and restarts the runner. Browse groups plugins into Game sources, Art & Metadata and Tools; Detected marks a launcher found on this host.

A plugin with its own page opens from its name under Plugins → Installed; pin it on that page to keep it in the sidebar. A plugin that keeps something per game adds a tab to each game's page in Library instead.

Badges and catalogs

Every catalog entry pins one exact version and its package hash, and the host re-checks that hash before it installs.

BadgeWhere it came from
VerifiedThe built-in unom catalog. unom reviewed this exact package.
External sourceA catalog you added. Pinned and hash-checked, but nobody at unom reviewed it.
UnverifiedInstalled from a package spec. Nothing pins it; you type the spec again to confirm.
Installed via CLIInstalled from the command line, so the host doesn't know where it came from.

A catalog can also withdraw a version: the console then refuses to install it and warns on Installed if you already have it.

To add a catalog: Sources → Add a catalog source, with a name, the index URL and optionally its ed25519:… public key. With a key set, the host accepts only a signed index from that source. Plugins from it are never Verified.

Install from the CLI

punktfunk-host plugins add rom-manager
punktfunk-host plugins enable           # only if the runner is off
CommandWhat it does
punktfunk-host plugins add <name…>Install plugins and restart the runner.
punktfunk-host plugins remove <name…>Uninstall plugins, drop their library titles, restart the runner.
punktfunk-host plugins listList installed plugins and their versions.
punktfunk-host plugins enableEnable and start the runner.
punktfunk-host plugins disableStop and disable the runner.
punktfunk-host plugins statusShow whether the runner is installed, enabled and running.
punktfunk-host plugins grant <plugin> <dir> [--write]Let a plugin reach a folder; read-only unless --write.
punktfunk-host plugins accessList grants, pending requests and denials.
punktfunk-host plugins revoke <plugin> <dir>Take one grant back.

A bare name installs the first-party package from Punktfunk's registry: playnite is @punktfunk/plugin-playnite. Any other name comes from the public npm registry and needs --allow-public-registry.

Where plugins run

On Linux each plugin runs in its own sandbox: no network unless it declared one, and only the folders it declared plus the ones you allowed. It can change only its own library entries. The sandbox needs bubblewrap, which the packages install, and unprivileged user namespaces. On Windows each plugin runs in an AppContainer of its own, under the low-privilege NT AUTHORITY\LocalService account: no network unless it declared one, and only the folders it declared plus the ones you allowed. Program Files stays readable to it; user profiles and other drives do not.

Each plugin also declares the programs the host may start for it. The host builds every command line itself, and nothing a running plugin sends can widen that list. Still, a plugin is code on your host: install only plugins you trust.

Scripts you put in ~/.config/punktfunk/scripts/ are your own code; they run in the runner itself, not in a plugin sandbox.

Give a plugin a folder

A plugin can't know where you keep ROMs or games. When it finds a folder it can't read, Library → Game sources shows the path and whether it wants read or write access. Choose Allow or Don't allow. Allowing restarts the plugin, or the whole runner for a folder in your home.

On Windows the runner can't read your user profile, so a launcher installed there looks like it isn't installed until you allow its folder.

A folder you type into a plugin's form, on its settings or on a game's tab, is allowed when you save. The form says so under the field. Only folders you add count, and the host still refuses your whole home, ~/.ssh and its own config folder.

Plugins → Installed lists every grant and denial: remove a grant there, or choose Ask again. The CLI does the same with grant, access and revoke. Use --write only when the plugin must change files in that folder. On Windows, write access can't be given inside Program Files or ProgramData: every plugin runs as the same account there.

Update and remove

  • Update: Update to <version> or Update all on Installed installs the catalog's version and restarts the runner. punktfunk-host plugins add <name> installs the newest version in the registry instead.
  • Remove: Uninstall, or punktfunk-host plugins remove <name>. Both drop the plugin's titles from the library and restart the runner. Its settings stay in plugin-state/<plugin>/ in the config directory, so a reinstall picks them up.
  • Stop every plugin: Disable runner on Installed, or punktfunk-host plugins disable. The host keeps streaming; you lose plugin game sources and automation.

ROM Manager

Point it at your ROM folders or a RomM server. Each game launches in the best emulator this host has for its system, after first-run setup and its firmware. A missing emulator is one Ask to install away; you allow it under Library → Sources. A RomM game downloads on its first launch with its updates and DLC, and its saves go back to the server. Install Libretro for box art and details, and SteamGridDB with an API key for the games Libretro has no art for.

punktfunk-host plugins add rom-manager

Add folders and servers on the console's ROM Manager page, under Sources. A server signs in with a code you approve in RomM. Without the console, write plugin-state/rom-manager/config.json in the config directory (~/.config/punktfunk or %ProgramData%\punktfunk):

{
  "roots": [
    { "dir": "/mnt/roms" },
    { "dir": "/mnt/more/ps1-games", "platform": "psx" }
  ]
}

A folder of system folders (snes, psx, ps2, …) needs nothing else; a folder of one system's games names it. Firmware goes in bios/<system>/ beside the system folders. On Windows, write paths as D:\\roms. Options: the plugin's repository.

Playnite

Mirrors your Playnite library; launching a title hands it back to Playnite. It has two parts, both on the Windows host:

  1. The plugin, from an elevated PowerShell: punktfunk-host plugins add playnite.
  2. The Punktfunk Sync extension in Playnite: download punktfunk-sync.pext from the plugin's builds, double-click it and restart Playnite.

The console's Playnite page then shows "Exporter connected", and games sync within seconds of a library change. Filters are on that page. Details: the plugin's repository.

VirtualHere (USB passthrough)

Hands a physical USB device on your couch machine (a wheel, HOTAS, pedals, an arcade stick) to the host while you play, so the game sees the real device. It drives VirtualHere, a commercial product you buy and install yourself; Punktfunk bundles none of it and isn't affiliated with it.

You need both halves of VirtualHere running first:

  • The USB Server on the couch, sharing the device. One device is free; more, or running the client as a service, needs a licence. Servers exist for Windows, Linux, macOS and Android, not for iOS or tvOS.
  • The USB Client on the host, ideally as a service.
punktfunk-host plugins add virtualhere

On the console's VirtualHere page, Devices lists what the couch shares; pick one to add a rule that matches it by name. By default the device moves to the host when video starts and back when it stops; you can widen that to the session or to the whole time a client is connected. If nothing happens, the Diagnostics tab names the part to fix. Details: the plugin's repository.

Game slots

Keeps a copy of a game's config or save files for each device that streams it and puts that copy in place before the game starts. Play at the PC with the monitor's settings, on the Steam Deck with the Deck's, on the TV with the TV's.

  1. Install Game slots from Plugins.
  2. Open the game in the Library and pick its Game slots tab.
  3. Turn on Keep a slot per device and add the game's config or save folders. On Windows, write the full C:\Users\<you>\… path.
  4. Save. The console gives the plugin write access to each folder you added.

A slot per settings preset gives one device a slot for each of its presets, like Docked and Handheld. The launch waits while the plugin swaps the files, up to two minutes; the device shows its launch screen meanwhile. When the game exits, the plugin saves the device's files into its slot and puts the PC's own back. A device's first launch starts from the PC's files.

It only swaps games a client launches from its library, never one already running, and only for paired devices. A folder that Steam Cloud syncs raises Steam's conflict dialog after a swap: track the game's config instead, or turn Steam Cloud off for that game. Details: the plugin's repository.

RTSS Frame Cap

Caps the game at the refresh rate the client streams at, through RivaTuner Statistics Server, and puts your own limit back when the stream ends. RTSS is a separate download; Punktfunk bundles none of it.

  1. Install RTSS and turn on Start with Windows in its main window. RTSS enforces the cap inside the game, so it must be running when the game starts.

  2. Install RTSS Frame Cap from Plugins, or run punktfunk-host plugins add rtss.

  3. Let the plugin write RTSS's profiles. Once, from an elevated PowerShell:

    icacls "C:\Program Files (x86)\RivaTuner Statistics Server\Profiles" /grant "*S-1-5-19:(OI)(CI)M" "*S-1-15-2-1251237843-3372458582-2949526732-893382375-1396029850-1946794802-2851191480:(OI)(CI)M"

    The first SID is the runner's account, the second the plugin's own sandbox; a write needs both. The console can't do this step: it never gives a plugin write access inside Program Files.

  4. Start the stream, then the game. A game that was already running keeps the limit it started with.

Nothing capped? Open Diagnostics → Troubleshooting and pick the plugin:rtss source: each missing step is a FAIL line with the command that fixes it, and each stream start logs rtss: 120 fps for …. A per-game profile in RTSS overrides the cap. Details and the doctor command: the plugin's repository.

Troubleshooting

The plugin runner isn't installed

The runner is its own package. Install it, then press Enable runner:

HostInstall
Ubuntusudo apt install punktfunk-scripting
Fedorasudo dnf install punktfunk-scripting
Archsudo pacman -Syu punktfunk-scripting
Steam Deck on-device buildRe-run scripts/steamdeck/install.sh
NixOSservices.punktfunk.scripting.enable (on whenever the host is)
WindowsRe-run the host setup

Installed but still reported missing? The host service can't find the punktfunk-scripting executable on its PATH. Point PUNKTFUNK_SCRIPTING at it (Configuration).

The plugin runner keeps failing to start

Read its log: journalctl --user -u punktfunk-scripting -f on Linux, %ProgramData%\punktfunk\plugin-state\runner.log on Windows. The console's Troubleshooting page shows plugin output too, under the plugin:<name> sources (plugin:runner for the runner). A log that ends with plugins can't be sandboxed here is the next entry.

Plugins can't be sandboxed here

The game library is empty and the Plugin runner card says Stopped. The runner can't build the sandbox each plugin runs in, so it stops at start instead of running one without it. Diagnostics → Troubleshooting → Health checks lists it as Plugins can't be sandboxed here or The plugin runner has stopped, with the reason.

Repair the sandbox, then start the runner. It stays stopped until you do.

HostRepairStart the runner
LinuxInstall bubblewrap 0.8 or newer, or allow unprivileged user namespaces.systemctl --user restart punktfunk-scripting
WindowsRe-run the host setup.Start-ScheduledTask -TaskName PunktfunkScripting, from an elevated PowerShell

As a last resort on Linux, Environment=PUNKTFUNK_PLUGIN_SANDBOX=off in systemctl --user edit punktfunk-scripting runs plugins with your whole account's access; the console warns while it is set.

A plugin can't read one of its folders

Open Library → Game sources and allow the folder there. The host refuses missing drives and protected paths outright; its log names each one (plugin folder request refused).

Plugin interfaces are unavailable

The console couldn't open its plugin port (TCP 47993 by default). Set PUNKTFUNK_UI_PLUGIN_PORT to a free port (Configuration) and restart the console.

Trust this plugin's port once

Your browser hasn't accepted the host's certificate for the plugin port, or the port is closed. See A plugin's interface doesn't load.

No plugin output yet

The runner isn't running. Check with punktfunk-host plugins status.

punktfunk-host: command not found

On Windows, open a new terminal or call the full path above. On Linux the packages install it to /usr/bin/punktfunk-host.

Writing a plugin

Plugins are TypeScript packages built on @punktfunk/plugin-kit. See Writing plugins.

On this page