Skip to main content
This is unreleased documentation for the next Zaparoo Core release.
For up-to-date documentation, see the latest version (Stable).
Version: Next

Service Config and Auth File

Part of the config file reference. Keys are shown with their type and default, followed by what they do and an example.

Service

[service]
api_port = 7497
api_listen = "0.0.0.0"
on_boot = "**execute:/media/fat/zaparoo/scripts/on-boot.sh"
on_ready = "**execute:/media/fat/zaparoo/scripts/on-ready.sh"
allowed_ips = [
"192.168.1.100",
"192.168.1.0/24"
]
encryption = false
device_id = '4d01c19f-09ba-4871-a58a-82fb49f5b518'
allowed_origins = [
'zaparoo.example.lan'
]
allow_run = [
'\*\*launch\.random:.+'
]

[service.remote_control]
enabled = false

[[service.publishers.mqtt]]
enabled = true
broker = "mqtt://localhost:1883"
topic = "zaparoo/events"
filter = [
"media.started",
"media.stopped",
"tokens.added"
]

api_port

KeyTypeDefault
api_portinteger (1024-65535)7497

api_port specifies which port the API of Core should be accessible from.

Don't change this unless you know what you're doing. It will currently break external tools that rely on it being the default value.

on_boot

KeyTypeDefault
on_bootstring

on_boot is a hook containing a snippet of ZapScript. It runs after Core initializes, only on the first Core start for the current operating system boot.

[service]
on_boot = "**execute:/media/fat/zaparoo/scripts/on-boot.sh"

If Core cannot detect boot state, on_boot is skipped and a warning is logged.

on_ready

KeyTypeDefault
on_readystring

on_ready is a hook containing a snippet of ZapScript. It runs after Core initializes on every service start. On platforms that report service readiness, Core waits for that readiness signal first.

[service]
on_ready = "**execute:/media/fat/zaparoo/scripts/on-ready.sh"

api_listen

KeyTypeDefault
api_listenstring"0.0.0.0"

api_listen specifies which network interface the API server should bind to.

[service]
api_listen = "127.0.0.1" # Localhost only

Common values:

  • "0.0.0.0" - Listen on all network interfaces (default, allows remote connections)
  • "127.0.0.1" - Localhost only (blocks all remote connections)
  • Specific IP address - Bind to a specific network interface

Use "127.0.0.1" if you only want local access to the API and Web UI.

allowed_ips

KeyTypeDefault
allowed_ipsstring[][]

allowed_ips creates an IP allowlist for remote HTTP API access. Localhost is always allowed. When this list is empty, remote HTTP and REST API requests are blocked by default.

WebSocket API routes are not controlled by allowed_ips. They use API key authentication when API keys are configured, or paired-client encryption when encryption is enabled.

[service]
allowed_ips = [
"192.168.1.100", # Single IP
"192.168.1.0/24", # CIDR range (entire subnet)
"10.0.0.0/8", # Large CIDR range
"2001:db8::1", # IPv6 address
"2001:db8::/32" # IPv6 CIDR range
]

Supports:

  • Individual IPv4 addresses (e.g., "192.168.1.100")
  • Individual IPv6 addresses (e.g., "2001:db8::1")
  • CIDR ranges for IPv4 (e.g., "192.168.1.0/24")
  • CIDR ranges for IPv6 (e.g., "2001:db8::/32")

Empty list (default): Localhost only for HTTP and REST API requests

Port numbers in IP addresses are automatically stripped during matching.

device_id

KeyTypeDefault
device_idstring (UUID)generated at first start

device_id is a UUID that is used to uniquely identify the instance of the Core service running on a host device.

Core uses it to link the device to a Zaparoo Online account, to spread update checks across devices, to name the device in discovery when it has no instance name and no hostname, and as the device identifier in cloud backups and error reports. It should not be changed once set. A restored backup keeps the original value.

allowed_origins

KeyTypeDefault
allowed_originsstring[][]

allowed_origins adds browser origins that are allowed to reach the Core API. An origin is the scheme, host and port a page was loaded from, like http://zaparoo.example.lan:7497. Browsers attach it to cross-origin API requests and to every WebSocket connection, and Core rejects any origin it doesn't recognize.

You need this when you reach the Web UI or the API through a name Core doesn't already know about, such as a hostname from your router or DNS server, a VPN address, or a reverse proxy.

What Core allows on its own

  • http://localhost and https://localhost, with and without the API port
  • http://127.0.0.1 and https://127.0.0.1 on the API port
  • Every private IPv4 address currently assigned to the device, on the API port. This is rechecked on each request, so an address that only appears after Core starts still works.
  • The device's own hostname and its mDNS .local name, over HTTP and HTTPS, with and without the API port. A device called mister covers http://mister:7497 and http://mister.local:7497.
  • The Zaparoo App origins: https://zaparoo.app for the hosted web app, plus capacitor://localhost and ionic://localhost for the mobile builds

Everything else needs an entry. That includes IPv6 addresses, public IPv4 addresses, addresses outside the private ranges such as Tailscale's 100.64.0.0/10, and any hostname you point at the device yourself.

Core won't trust a name just because it resolves to the device. If it did, any site you visited could publish a DNS record aimed at your device and drive Core from a browser tab, an attack called DNS rebinding. The allowlist is what stops it, so it stays explicit.

Entry formats

A bare hostname is the most forgiving form. It expands to four origins, covering HTTP and HTTPS with and without the API port:

[service]
allowed_origins = [
'zaparoo.example.lan'
]

That one entry allows http://zaparoo.example.lan, https://zaparoo.example.lan, http://zaparoo.example.lan:7497 and https://zaparoo.example.lan:7497.

Include a scheme and Core matches more narrowly:

EntryWhat it allows
zaparoo.example.lanHTTP and HTTPS, with and without the API port
http://zaparoo.example.lanhttp://zaparoo.example.lan and http://zaparoo.example.lan:7497
https://zaparoo.example.lan:8443https://zaparoo.example.lan:8443 and nothing else
capacitor://localhostExactly as written, with no port variant added

The port that matters is the one in the browser's address bar, not the one Core listens on. A reverse proxy serving https://zaparoo.example.lan on the default HTTPS port sends an origin with no port, so a bare hostname or https://zaparoo.example.lan covers it. If the proxy listens somewhere else, add that port explicitly.

Matching ignores case, and surrounding whitespace and a trailing / are trimmed from each entry. IPv6 literals need a scheme and square brackets, like http://[fd12:3456::1]:7497.

If the Web UI loads but never connects, see Web UI troubleshooting.

encryption

KeyTypeDefault
encryptionbooleanvaries by platform

encryption requires remote WebSocket API clients to use the paired-client encryption flow. Localhost connections are always allowed without encryption.

When the key is not set, Core uses the platform default: true on Linux, SteamOS, and Windows, false everywhere else. A value you set yourself is kept, including through a backup restore.

[service]
encryption = true

Paired clients can have admin or member permissions. Requiring encryption makes these restrictions enforceable because every remote client must identify itself through pairing. When encryption is off, MiSTer, Batocera, LibreELEC, and RePlayOS still let unpaired remote clients use the API; every other platform requires remote clients to pair or send an API key regardless. Manage pairing, roles, and this setting under Settings > Clients in the terminal UI.

allow_run

KeyTypeDefault
allow_runstring[] (regex patterns)[]

allow_run explicitly allows ZapScript to be run using the run endpoint of the Core API. By default, nothing is allowed.

Each entry is a Regular Expression. Notes on usage here:

  • Patterns are automatically anchored and must match the full command string. Characters * and . common in ZapScript must be escaped (e.g., \*\*launch\.random:.*).
  • The input is parsed as ZapScript and each command is checked individually. All commands in a chained script must match, so a pattern like \*\*launch\.random:.* covers both **launch.random:SNES alone and **launch.random:SNES||**launch.random:NES.
  • Plain file paths are normalized to a launch command before checking.
  • When allow_run is configured, remote IPs can access run endpoints regardless of allowed_ips. The allow_run patterns still restrict which ZapScript can run.

service.discovery

service.discovery is a sub-section of service that configures mDNS network discovery.

[service.discovery]
enabled = true
instance_name = "Living Room MiSTer"

When enabled, Zaparoo advertises itself on your local network using mDNS/DNS-SD. Mobile apps and other clients can automatically find and connect to your Zaparoo instance without needing to enter IP addresses manually. Access Zaparoo using friendly .local addresses like http://mister.local:7497.

enabled

KeyTypeDefault
enabledbooleantrue

enabled enables or disables mDNS network discovery. When enabled, Zaparoo advertises a _zaparoo._tcp service on each active network interface.

instance_name

KeyTypeDefault
instance_namestringsystem hostname

instance_name specifies a custom display name for this Zaparoo instance on the network. If not set, defaults to the system hostname.

service.remote_control

service.remote_control is a sub-section of service that lets apps you authorize through the Zaparoo Online User API send approved commands to this device. It is off by default, and linking a Zaparoo Online account does not turn it on.

[service.remote_control]
enabled = true

enabled

KeyTypeDefault
enabledbooleanfalse

enabled turns remote control on. Unlinking the device turns it off again. You can also change it under Settings > Online in the terminal UI.

base_url

KeyTypeDefault
base_urlstringhttps://api.zaparoo.com

base_url is the Zaparoo Online endpoint Core polls for remote commands. Leave it at the default unless you run your own compatible server.

service.publishers

service.publishers is a sub-section of service that configures publishing Core events to external services.

service.publishers.mqtt

service.publishers.mqtt configures MQTT publishers for broadcasting Core events. See the Publishers feature page for an overview. It's a sub-section that can be defined multiple times, and must have this header: [[service.publishers.mqtt]]

Pay attention to the double pairs of square brackets. Each defined MQTT publisher section must have its own header.

[[service.publishers.mqtt]]
enabled = true
broker = "mqtt://localhost:1883"
topic = "zaparoo/events"
filter = [
"media.started",
"media.stopped",
"tokens.added"
]
enabled
KeyTypeDefault
enabledbooleantrue

enabled turns this MQTT publisher on or off.

[[service.publishers.mqtt]]
enabled = false
broker
KeyTypeDefault
brokerstring

broker specifies the MQTT broker connection URL.

[[service.publishers.mqtt]]
broker = "mqtt://192.168.1.100:1883"

Supported URL schemes:

  • mqtt:// - Standard MQTT connection
  • mqtts:// or ssl:// - MQTT over TLS/SSL

For TLS connections and authentication, see the MQTT reader's auth.toml configuration.

topic
KeyTypeDefault
topicstring

topic specifies the MQTT topic to publish events to.

[[service.publishers.mqtt]]
topic = "home/zaparoo/events"
filter
KeyTypeDefault
filterstring[][] (publish all events)

filter limits which event types are published. When empty, all events are published.

[[service.publishers.mqtt]]
filter = [
"media.started",
"media.stopped",
"tokens.added",
"readers.added"
]

Available event types match the Core API notification types.

service.publishers.pixelcade

service.publishers.pixelcade configures PixelCade publishers that display game marquee artwork on PixelCade LED displays. See the Publishers feature page for an overview. It can be defined multiple times and must use this header: [[service.publishers.pixelcade]]

On media.started, the publisher sends a GET request to the PixelCade arcade endpoint, mapping the Zaparoo system ID to the matching PixelCade console folder.

[[service.publishers.pixelcade]]
enabled = true
host = "192.168.1.50"
port = 8080
mode = "stream"
filter = [
"media.started"
]
enabled
KeyTypeDefault
enabledbooleantrue

enabled turns this PixelCade publisher on or off.

[[service.publishers.pixelcade]]
enabled = false
host
KeyTypeDefault
hoststring

host is the hostname or IP address of the PixelCade device. This field is required.

[[service.publishers.pixelcade]]
host = "192.168.1.50"
port
KeyTypeDefault
portinteger8080

port is the PixelCade HTTP API port.

mode
KeyTypeDefault
modestring"stream"

mode controls which PixelCade arcade endpoint is used when displaying marquee art on media.started. Accepted values:

  • "stream": uses the streaming endpoint (default)
  • "write": uses the write endpoint
filter
KeyTypeDefault
filterstring[][] (publish all events)

filter limits which event types trigger requests to PixelCade. When empty, all events are forwarded to the publisher. Only media.started produces PixelCade requests; all other notification types are ignored.

[[service.publishers.pixelcade]]
filter = [
"media.started"
]

Available event types match the Core API notification types.

Auth File

A separate TOML file called auth.toml, alongside the config file, can be created which defines credentials for remote connections.

API Keys

Warning

API key authentication is a basic access control measure, not a security feature. Connections are unencrypted and keys can be intercepted by anyone on your network.

API key authentication restricts remote access to the Zaparoo API:

api_keys = ["your-secret-key-here"]

Authenticate using either a Bearer token header or query parameter:

Authorization: Bearer your-secret-key-here
http://zaparoo:7497/api/v1/launch?key=your-secret-key-here

Localhost connections are always allowed without authentication, so the CLI and TUI continue to work without configuration.

Remote Credentials

Define credentials used when Core connects to remote endpoints:

["smb://10.0.0.123/Games"]
username = "myaccount"
password = "Password123"

URL Matching

When a remote endpoint matches against the URL key, the credential set will be attached to the request. Matching uses a 3-step fallback:

  1. Exact scheme match - scheme, host, and path prefix must match exactly
  2. Canonical scheme match - equivalent schemes match (e.g., tcp:// matches mqtt://)
  3. Schemeless match - entries without a scheme match any connection to that host:port
# Matches only mqtt:// connections
["mqtt://192.168.1.100:1883"]
username = "mqtt_only"
password = "pass"

# Matches any connection to this host:port (mqtt, mqtts, tcp, etc.)
["192.168.1.100:1883"]
username = "any_scheme"
password = "pass"

Scheme Aliases

These scheme aliases are recognized:

AliasCanonical
tcp://mqtt://
ssl://mqtts://
ws://http://
wss://https://

Multiple credentials may be defined for the same server but on different paths.