dkulp
Full time elf
FPP 10.3
Release date: 22 October 2026FPP 10.3 is a point release on top of 10.2 — 845 commits from 12 contributors in the two and a half weeks since 10.2 shipped. The theme of this release is hardening: we audited fppd, the web UI and the boot tools subsystem by subsystem looking for race conditions, deadlocks, crashes and security holes, and several hundred of these commits fix what that turned up. Alongside it, a brand new regression test suite now runs almost 2,000 tests every night covering most of the changes since FPP 10.0 and the issues we found. The secondary theme is media/light synchronization: lights now follow the audio to within about 15 ms, no longer drift over a song, and MultiSync remotes play in step with the master to within a few milliseconds.
NOTE — PHP security fix: please update and reboot. On October 21 PHP disclosed two critical flaws, known as PHP2Shell (CVE-2026-109618 and CVE-2026-109619), that let anyone who can reach a web server running PHP run code on it without logging in. FPP's web UI runs on PHP, so every player is affected until its PHP is updated. The OS level moves from 2026-10 to 2026-10b for the fixed PHP, and we recommend applying the 10.3 OS update (fppos), which brings the fixed PHP together with the other OS-level security updates. A normal FPP update to 10.3 also installs the fixed PHP from your distribution and asks for a reboot; a player that can't reach the package servers at that moment retries after each boot and daily until it can. Either way, reboot when asked so the web UI runs the fixed PHP.
32-bit Pi image: Raspberry Pi OS's 32-bit package archive has not published the fixed PHP yet, so the 32-bit Pi image and fppos still contain the older PHP. On those players FPP turns off PHP's upload-progress feature (PHP's own workaround for one of the two flaws; FPP never uses it) and installs the fixed PHP automatically, with a Reboot Required prompt, as soon as it is published. The 64-bit Pi, BeagleBone Black and PocketBeagle 2 images already contain the fix.
Behavior changes worth knowing before you update (details under Deprecations & Behavior Changes):
- Other websites can no longer read the FPP API from your browser by default. xLights, fppMon and other non-browser tools are unaffected; a new "Let other websites read the API" setting turns browser access back on if you need it.
- API routes that change something with a GET are deprecated. They still work in 10.x but now say so in a header and the log; FPP 11 will require POST.
- After an FPPOS reflash, native plugins built for the old FPP are not loaded until they are rebuilt or reinstalled.
- File Manager's MP3Gain button now actually changes the volume of the files instead of only analyzing them.
Hardening
The new fpp-tests suite starts a real fppd, built with AddressSanitizer or ThreadSanitizer, drives it over its HTTP API the way the UI, xLights, MQTT and MultiSync do, and fails on a crash, a wedge, a memory error, a data race or a wrong answer. About 1,480 scenarios run nightly under both sanitizers and in a coverage build, another 474 tests drive the web UI in a real browser, and a hardware set runs on a Raspberry Pi 5. Together they cover most of the changes since FPP 10.0 and most of the problems fixed in this release, so those bugs stay fixed. Results, coverage and the parts of FPP no test reaches yet are on the test dashboard — if you'd like to help, that page shows where a new test would do the most good.
Bad configuration no longer takes fppd down
- A wrong-typed value in a hand-edited, restored, plugin-written or API-written config no longer crashes fppd or leaves it crash-looping at startup — for example
"enabled": "yes", a number saved as a string,nan, or a list where one value belongs. This covers channel outputs and output processors, playlists and dynamic playlist entries, the schedule, command presets, variables, sensors, GPIO inputs, recurring tasks, bridge inputs, PipeWire audio groups, AES67/Opus/RTSP/video configs, stream slots, overlay models and xLights submodel/model-group files (such as an upload from FPP Connect). The bad entry is skipped and a new "Configuration Entry Skipped" warning names it; the warning clears once the file is fixed. - Out-of-range values are refused instead of crashing or corrupting memory: output processors, pixel strings and network universes running past the last channel; panels whose output, chain, offset, scan or interleave don't fit; too many outputs for BBB48String, BBBSerial, BBBMatrix, DPIPixels, MAX7219, PCF8574, MCP23017 or USB Pixelnet; Falcon smart-receiver port numbers; and virtual display map pixels outside the buffer. A gamma of NaN no longer runs a string or the whole show at full brightness.
- fppinit no longer stops part way through boot setup on a malformed config file or a system without
/etc/wpa_supplicant, which left network interfaces unconfigured. - A malformed
.fseqor.eseq(truncated upload, full disk, crafted sparse ranges) no longer overflows memory; the unreadable part plays as dark frames instead of random memory on the lights, with one log line instead of one per frame. - An oversized image (a small PNG claiming 30000x30000) can no longer exhaust memory; the image limit scales with the board's RAM.
Reloads, restarts and commands from several places at once
- Saving channel outputs while a show runs is safe: fixed crashes and wedges when a reload overlapped a threaded output sending its last frame, a controller ping, overlay models in use (bulk pixel uploads, effects, the WLED API, image playlist entries), a virtual display drawing, a blank from a playlist stop, or a slow send to a dead controller. A reload no longer waits for unrelated HTTP requests (frames stopped for up to about 6 s), and output processors swap in all at once, so a frame is never sent without its brightness or current-limit processor.
- Media commands from several sources at once no longer crash fppd — Play Media, Stop Media, Stop All Media, Start Playlist, Stop Now, Insert Playlist Immediate and Set Slot Volume arriving together from the UI, API, MQTT and playlists. Starting and stopping playlists from two places at once, two sequence opens at once, and commands arriving while an inserted playlist starts are handled correctly.
- Commands that loop back into themselves stop at 32 levels with a log line instead of crashing fppd: presets that trigger themselves (including a MEDIA_STARTED preset that plays media), If Then/Else lists,
%VAR:expansions, and a Restart/Next/Prev Playlist Item entry that lands on itself. A scheduled hard stop whose Playlist Stopped preset starts another playlist no longer loops forever. - A malformed command no longer crashes fppd: wrong-typed MQTT
/set/commandfields, unknown colour names, unparseable arguments, over-long expressions, malformed command presets and bad overlay effect arguments. - fppd starts even when another program holds the E1.31 or DDP input port, exits promptly so systemd can restart it after a crash (instead of the show going dark for two minutes), and no longer crashes or deadlocks at shutdown.
- Many data races found by ThreadSanitizer are fixed across the output thread, playlists, media status, overlays, MQTT, timers, ping, the Virtual Display, the bridge and MultiSync stats.
- The main loop no longer stalls behind a slow webhook, a slow command preset, a slow virtual display preview browser, a YouTube lookup, a PipeWire daemon that stops answering, an image entry with an empty folder, a dynamic playlist fetching its items, or a flood of E1.31/DDP/ArtNet input or discovery pings.
Clock steps
- A wall-clock jump no longer freezes or misfires FPP. A clock step (an NTP correction, or a cape RTC that has never been set booting the clock back to 2000) could stop every output frame for the size of the step, hold back or fire all at once pending timers (Trigger Command Preset In Future, recurring tasks, GPIO debounce/hold), freeze overlay effects and test patterns, stop media mid-show through the stall watchdogs, stop controller pings and the LOR heartbeat, and leave a startup playlist's Pause waiting for decades. All interval timing now uses a monotonic clock, and schedules that ran early on a board booted with its clock ahead run again at their real time.
Untrusted network input
- A device on the LAN can no longer crash fppd with a malformed MultiSync or discovery answer, a crafted page during controller detection, a crafted WLED audio-sync packet, wrong-typed WLED app requests, or a flood of announcements. Discovery state, MultiSync stats, Twinkly probes and the MQTT message cache are now bounded.
- Pi 4 (and others): pixel output no longer freezes until a power cycle when Wi-Fi stops transmitting mid-show — MultiSync multicast/broadcast sends never block (#3072).
- UDP outputs to a controller whose frames exceed its pacing rate no longer spawn threads until fppd crashes, and a saturated controller with many universes no longer runs fppd out of file descriptors.
- With MultiSync broadcast on, FPP commands sent to all hosts no longer run twice on the sender (including on a player with both Ethernet and Wi-Fi up).
Plugins
- A plugin that throws, hangs or is unloaded mid-call can no longer take fppd down: exceptions from plugin API handlers return HTTP 500, a script plugin that hangs at startup is skipped after 10 s, and a plugin unloaded while another thread was in its callback is no longer called after being freed.
Warnings and logs
- Warning banners now come down when the problem is fixed instead of staying until fppd restarts — output and panel config warnings, Cannot Ping for a removed controller, frame skips on a slow network, schedule warnings, audio card not present, media and video pipeline warnings, RTSP and Opus warnings, receiver eFuse and pixel-count warnings, and the Pi low-voltage warning.
- Many log-every-frame and log-every-packet messages are rate-limited, which protects SD cards (one player had written 360 MB of fppd.log). Log lines containing
%no longer print garbage, and JSON parse errors no longer dump request bodies with credentials into the log.
Security
- PHP2Shell (CVE-2026-109618, CVE-2026-109619): the 10.3 images and fppos carry the fixed PHP (32-bit Pi: see the note at the top), and the normal update installs it on existing players, retrying until it can and asking for a reboot. PHP's upload-progress feature, which FPP never uses, is turned off on every player as PHP's workaround for one of the two flaws.
- The API no longer returns stored passwords and keys (OS login, UI password, GitHub token, MQTT and email passwords, WiFi keys), and the file APIs no longer expose the UI password hash.
- Other websites can no longer change a player from your browser: cross-site POST, PUT and DELETE requests are refused, and cross-site reads of the API are off by default.
- Closed a UI password bypass: a request sent through
/proxy/back to the player's own address counted as "local" and skipped the password. The update fixes existing installs. - The UI password is hashed with bcrypt on multi-core boards (Pi 5, Pi 4, PocketBeagle 2); an existing password is re-hashed the next time it is saved.
- The URL command fetches only http/https (it could read local files as root), and video input URLs are only passed to yt-dlp if they are http(s).
- Cape EEPROMs, including uploaded virtual EEPROMs, are treated as untrusted: a crafted or unsigned image can no longer write files outside its folder, run commands, rewrite boot config beyond
dtoverlay=/dtparam=lines, or change the EEPROM-signing host. - Closed command-injection and configuration-injection holes through settings that reach root shell commands (host name, email, time zone, HDMI, WiFi region and more), the I2C sensor address, the ALSA mixer name, PipeWire/audio page names, media pipeline settings and the network config APIs.
- Closed cross-site scripting holes through scanned WiFi names, file, song, sequence and playlist names, warning text, setting values, MultiSync remotes posing as FPP or Falcon controllers, and other untrusted strings shown in the UI (#3051, #3105).
- Uploads refuse server-executable file types; files served through the file API and the 3D virtual display assets folder are sandboxed; script, playlist, sequence and overlay image names can no longer reach outside their folders; and deleting with an empty name no longer deletes a whole media folder.
- The MultiSync remote helpers and
/api/remoteActioncontact only other players, never this player's own local-only services, and no longer follow redirects. Restoring a backup no longer runs scripts' install steps as root, and the setup wizard's restore is accepted only before setup is complete.
Privacy
- Crash and diagnostic report redaction now also catches more credential formats (curl
-u,--passwordflags, cookies, session ids, common API keys) and coordinates, and the cape serial number is left out everywhere when serial-number sharing is off. - An FPPOS reflash no longer copies a plugin's GitHub token into settings and backups.
Media & Light Sync
- Lights now follow the audio to within about 15 ms instead of 20-50 ms, consistently from one show start to the next, with frame timing corrected smoothly so the cadence never visibly jumps (#3083).
- Lights no longer drift behind the music over a song: frames are scheduled on a fixed cadence, so a five-minute song on a Pi 5 that used to end about 58 ms behind now ends on time.
- Media through PipeWire is paced by the sound card's own clock: a fast card no longer plays short dropouts every half-minute or so, and a slow card no longer lets the sound fall behind the lights. No resampling is added.
- Sound no longer falls progressively behind the lights over a long scheduled show (about 250 ms after an hour on a Pi 4) after an audio stall or xrun; the audio is realigned within a quarter second (#3083).
- MultiSync remotes play their own copy of the show audio in step with the master — a remote gently speeds up or slows down its audio (within 0.5%, inaudible) and lands within about 10 ms of the master on Pi 5, Pi 4 and PocketBeagle 2, instead of hunting or settling 0.4-0.6 s behind.
- MultiSync remotes' lights track the master's to within a couple of milliseconds (a remote could sit up to two frames late depending on how it started), and late sync packets no longer make remote lights jitter. Remote Offset now moves a remote's lights by the exact milliseconds set rather than rounding to a whole frame.
- Video sound plays through the same audio output as music files, so a video's picture, sound and lights stay together, and on a remote a video's sound holds with the master instead of drifting about 180 ms behind.
- Resuming a VBR MP3 after Insert Playlist Immediate no longer leaves the audio 0.1-11 s off from the lights for the rest of the entry (#3087).
- Only the show's own media now paces the sequence; Play Media on another slot or a companion stream used to pull the sequence between two clocks.
- Fixed: a playlist's next sequence stuttering its first frames after the previous one's blanking; a sequence occasionally playing its whole length at the bridging rate (a 40 fps sequence at 20 fps); a media offset shorter than one frame playing the song with no lights; a sequence whose media failed to start once playing lights only on later passes; a remote restarting its media over and over with a media offset and MultiSync; a resumed entry's companion slot playing from 0 for about 4 s; FSEQ frame-trigger presets firing 2-3 times or not at all; and HDMI/framebuffer/DPI outputs stalling media processing during each display refresh.
- New Media Files tab on the Troubleshooting page lists each media file's codec, sample rate, bit rate, length and size, and shows which audio files the CPU has to resample because their rate differs from the audio engine's — a cost on the smaller boards.
Enhancements
Audio & Video
- HDMI outputs survive the display being unplugged and plugged back in, and an HDMI output whose display is off at boot (a projector switched on later) comes up anyway, switching to the display's own mode when it appears. A new warning says when no display is detected (#3102).
- Sound cards are found on non-English systems — FPP installed on a stock Debian with a non-English locale listed no audio devices (#3118). FPP OS images were not affected.
- Video inputs restart themselves when a camera reboots or a stream drops, and AES67, Opus and video streams that fail to start (PipeWire not up yet at boot) are retried automatically instead of staying down until Apply.
- Virtual matrix/framebuffer outputs and video playback can share one display card, and a camera group on an HDMI display steps aside while a video plays there and comes back afterwards.
- Dragging the volume slider no longer stalls light output.
- macOS: media plays through the same GStreamer pipelines as on Linux, with HDMI video in an "FPP Video Output" window.
Plugins
- Upgrading a plugin reloads it in the running fppd instead of requiring an FPPD restart, when its code actually changed.
- The Plugins page accepts a plain GitHub repository address as well as a pluginInfo.json URL.
- Package Manager has an "Install All Missing" button (for example after an OS upgrade), the plugin update check names the plugins whose check failed (#3046), and the Unknown Plugins health check names the plugins behind it.
MultiSync & Discovery
- Fleet actions no longer lock up the browser: Upgrade, OS Upgrade, Copy Files and Change Branch run over one connection, finish even if the page is closed, and report a failure with its reason instead of "Done", with Retry for a single failed system.
- Remotes that are mid-update show as "Upgrading" with the stage, and clicking one opens a live view of its update log that follows it across its restart.
- Copy Show Files gains a "Copy Images" option (#3095).
- The MultiSync receive buffer holds about ten times as many packets, so a large show coming up at once no longer drops sync and ping packets, and MultiSync pings carry the sender's UUID so two controllers with the same host name are told apart.
Updates & Upgrades
- Update progress survives a page refresh and shows on any browser, with a spinner in the header, progress on the About page and a banner when it finishes; a second update can't be started while one runs (#3070).
- A power loss right after an update no longer leaves fppd zero-filled and unbootable — builds are synced to disk, and the About page detects a damaged build and offers Rebuild FPP (#3120).
- When a newer release is offered, the About page also offers "Update to latest" on the version you are running.
Capes & Hardware
- PocketBeagle 2: DMX, Renard, LOR and LOR Enhanced outputs work on PocketBeagle capes (such as the K8-PB) whose line driver sits on a pin with no UART transmit on the PB2 — a PRU sends the serial stream on that pin, with no config change needed.
- Pi 5: a new "RPI Power Supply" readout on the Troubleshooting page shows the measured input voltage, estimated power, supply rating, USB current limit, throttle flags and temperature (#3077).
- Current-based pixel counting reliably counts low-current (seed) pixels on noisy current sensors, including PocketBeagle 2 capes, and no longer counts null nodes.
- Compute Module 4/5: a setting chooses the on-board or external WiFi antenna.
- Pi 5 on new 64-bit images: a Developer setting can switch to the 16K-page kernel.
Pixel Strings & Serial Outputs
- LOR Enhanced sends only the pixels that changed, paced to the serial line rate, so many more units fit on one network (16 units of 170 pixels at 1 Mbps and 40 fps stay in sync), and units that miss a packet recover within a second. The setup page defaults to 1 Mbps and calculates the channel count from the units.
- USB serial adapters can be picked by a name that survives reboots (the
/dev/serial/by-idor USB socket name), and xLights can read those names from the API. - LOR outputs stay in sync between sequences and are budgeted to the 19.2k line rate, so fades no longer trail the audio.
- ColorLight 5A-75 output does its per-frame work in half the time on a BBB, with identical output.
Scheduler, Playlists & Commands
- A schedule entry on a lunar holiday past the end of the built-in tables (2035) raises a warning instead of silently moving the show.
- Set Slot Volume on an idle media slot keeps the volume for that slot's next start.
- The playlist editor asks Save / Discard / Cancel before you leave with unsaved changes.
Diagnostics & Web UI
- A new warning appears when a unicast controller needs more bandwidth than its pacing rate allows (stuttering lights), with a link to fix it.
- Troubleshooting commands can be hidden by UI level, with a Dev Tools tab at Developer level (#3062).
- Location Lookup falls back to a second service when the first is rate limiting (common on cellular networks).
Performance
- Audio playback on single-core boards uses much less CPU: an mp3 on a BBB drops from about 30% to 18% of the core.
- The status API that every open page polls drops from about 566 ms to 151 ms on a BBB.
- Faster startup with no network configured, faster
fppdstop, and much less work when reloading E1.31/DDP/ArtNet configs with many destinations.
Developer
- Every state-changing API route now also accepts POST, and FPP's own UI, fppd, fppoled and the
fppCLI use it. /fppdws?quiet=1lets monitoring clients skip status frames where only the clock changed.- Plugins using FPP's HTTP helpers get a failed request when a transfer is cut off or times out, instead of a truncated body reported as 200.
fpp --log-levelworks again.
Continued next post...