// OPENBOX DOCS
Changelog
Release notes for OpenBox, from the latest release back to the first build.
1.15.0 (2026-09-29) — Finish the surface
The features that arrived without a screen finally have one, and the ones that had a screen got fixed. Motion respects your system setting, the light theme is readable, and nothing hides behind a dialog.
- Motion that respects you: dialogs now animate out as well as in, switching themes cross-fades instead of snapping, covers reserve their space and fade in rather than collapsing and jumping, and typing in search no longer replays the whole grid's entrance. Every duration and easing is a token, and one
prefers-reduced-motionblock zeroes all of them, so a system "reduce motion" setting makes the whole interface static — including the Game Night wheel, the Constellation layout, and the page header, which previously kept animating. The busy button state shows an ellipsis instead of a frozen spinner. See Themes. - A light theme you can actually read: Harbor Light had white text on white inputs, card titles, and the active nav item, and insight cards were dark on dark. Text now uses semantic ink tokens (
--ink-strong,--on-active,--on-danger,--border-input,--state-hover,--state-press), and a contrast matrix in the test suite now checks every theme rather than trusting each one to be right. - High Contrast theme: a sixth stock theme, built by redeclaring every
:roottoken at maximum contrast — 21:1 body text and AAA foregrounds. It ships with OpenBox; see Themes. - Toasts that stay put: notifications now render in the browser's top layer, so they sit above dialogs and Big Box instead of behind them, and an Undo toast is never overwritten by a message that arrives while it is showing. Screen readers hear them through a separate always-present live region.
- Emulator definition updates: Settings → Emulators can check for, install, and roll back a signed community definition pack. The pack is verified with the release Ed25519 key before a single file is written, installs into your data directory where it shadows the bundled set without overwriting it, and a signature failure raises a security notification instead of failing quietly. A definition you already have is never clobbered, and rollback removes exactly what the pack installed. Until a pack is published the panel says so plainly — it is not an error. See Emulators and launching.
- Time Machine Compare: a new Compare tab in Tools → Time Machine. Pick one date or two and see which games were added, removed, and changed, field by field, with true totals ("showing 200 of 1,340") and a warning when the start date predates the kept history. It is read-only, and revert stays exactly as bounded as before. See Library Time Machine.
- Settings → About: the version you are running, the platform, your data folder, and whether OpenBox is in its native window or a browser tab — read from the same host report the window itself uses. See Windows.
- Windows uninstaller:
scripts/uninstall.ps1removes exactly what the installer creates — the install tree and its rollback copy, the userPATHentry, the Start Menu shortcut, and theopenbox://registration. Your library and settings are never touched, and-WhatIfpreviews the whole thing. See Windows. - Better for everyone: the card grid is announced as a list with the real size and position of every card; Big Box, its menu, the pause panel, and the screensaver announce themselves as dialogs; job rows expose their progress; the result count is a live region; touch targets are larger; forced-colors system themes get outlines; and the "No games match this view" state has a Clear filters button.
- Story PNG export: a per-game Story card can be exported as a PNG, rendered entirely in the browser with no new endpoint and no new dependency.
- Fixed:
- The light theme opened dark on some starts; the last theme is now remembered and applied before the page paints.
- A circular import could leave the library empty on a cold start in some browsers.
- The Constellation layout kept computing after its dialog closed, and ignored the reduced-motion setting.
- Covers collapsed to zero height and jumped on load; the box is reserved and a failed cover falls back to the title tile.
- Lightbox arrows jumped about 17 px on mouse-down, and busy buttons blanked their label instead of trailing a spinner.
- The workspace clipped between 1101 and 1119 px wide.
Full OpenBox 1.15.0 release notes · Compare v1.14.0...v1.15.0
1.14.0 (2026-09-22) — Library intelligence
The library starts looking after itself: a health score that names every problem, artwork that can be repaired in one batch, a search that understands what you mean, and a personal backlog layer that is yours rather than the database's.
- Library health score: a 0–100 score with five weighted dimensions — file integrity 35, duplicates 20, artwork 20, metadata 15, launch readiness 10. Every deduction names the games behind it. The audit dialog gains a health card and a per-dimension breakdown, each with a Fix all in this dimension queue that previews first and stays undoable, a Big Box health tile with re-scan, and a scheduled rescan (daily, weekly, on startup, or off; default weekly) that skips an active game session. See Organizing.
- Artwork Doctor: scans the library for missing covers, missing game or media files, low-resolution and wrong-aspect artwork, and duplicate images, then fixes them all with SteamGridDB in one cancelable batch. Every replacement records which provider it came from, and the whole batch is undoable.
- Repair missing files: a wizard scans for missing game and media paths, matches them against a folder you pick, and relinks only the matches that are still missing — anything that changed since the scan is skipped rather than guessed at.
- Duplicate merge: duplicate detection now groups identity, path, and title collisions, and the merge preview picks the record with the most play history, unions media and list fields, and moves the absorbed entries to the Trash so a merge is reversible. The health dialog's dedupe button opens it.
- Game DNA search: the sidebar search box gains a Title | Smart toggle (default Title, remembered). Smart mode is fully offline — BM25 plus a curated 151-concept lexicon in English, German, Spanish, French, and Portuguese, with 1024-dimensional feature hashing — and shows "why" explanation chips next to each hit. More like this is available from the details pane, the card context menu, and Big Box. There is no AI cloud and nothing is downloaded; the index is a local sidecar file excluded from sync.
- Plugins 2.0: per-plugin checksum-bound trust instead of a global toggle, Android-style permission prompts at install and enable time (a
networkpermission is what grants--share-net), per-plugin settings forms generated from a manifest JSON Schema subset (text, number, boolean, enum, password) and delivered to hooks aspayload["settings"], alibrary_sourceimporter hook that merges plugin games into the library behind a source badge, a singleeventslifecycle hook (app_startup,app_shutdown,scan_finished,playtime_milestone,game_added,game_removed,game_updated), a catalog browser tab with per-entry Install/Update, and plugin commands in the command palette under the>prefix. Plugin API v1 is frozen: manifests declareapi_versionand a newer version is refused with an error rather than guessed at. See Plugins guide. - Backlog management: every game gets a personal backlog layer on top of the existing progress field. Status shows as "Unplayed" wherever it is unset; "Abandoned" covers the dropped case. The query grammar gains
progress:unplayed,my 4 stars,unrated by me, andmyrating:4as their own clause kinds, distinct from the metadataratingpredicates. A personal star rating (user_rating, 0–5, separate from the metadata rating) gets hover stars on cards and in details, a card badge, a "My rating" sort, a "My rating" bulk field, and picker weighting that favors highly-rated unplayed games with a stated reason. Manual playtime sessions (date, seconds, note) can be logged, edited, and deleted from the details pane and count toward stats and totals, while streaks still count observed sessions only. Notes are dated entries with add/edit/delete. An optional one-time Mark as Playing? prompt on first launch can be switched off in Settings and never sets progress silently. - Effortless metadata: the setup wizard's Options step gains Automatically match metadata and download media after import (on by default) plus per-provider opt-ins for ScreenScraper, IGDB, and SteamGridDB (all off by default). One import queues exactly two jobs: an offline LaunchBox match preview plus the opted-in hash and exact-title passes, then media for confidently matched games and a SteamGridDB fill for whatever is still missing. Every provider respects its own budget, and one game's failure never aborts a pass. Settings live at
GET/POST /api/v2/metadata/scrape-settings; unchecking every media type runs match-only. - ScreenScraper ROM-hash confidence: an independent MD5-only and a CRC32-only lookup must agree on the same ScreenScraper game id before anything is applied automatically. Weaker evidence stays review-only, and
/api/v2/screenscraper/matchacceptsapply_confidentfor bulk runs. - Thumbnail chooser: every LaunchBox, SteamGridDB, and ScreenScraper search result now offers a Thumbnails button that opens a chooser with all image candidates grouped by media kind, so you can pick exactly the one you want instead of the provider's top pick. A chosen URL is only downloaded when it matches a candidate the provider returned for that record, so a client cannot make the server fetch an arbitrary URL.
- Big Box boot and on-screen keyboard: a Start in Big Box mode checkbox in Settings → Big Box (
bigbox_start_at_launch) and a--bigboxflag open the UI straight into Big Box, so a Deck or HTPC boots into the shelf instead of the desktop grid. The hybrid search field has a d-pad-navigable QWERTY and symbols keyboard that types into the existing filter and sits on the gamepad loop just below the pause overlay. - Steam Bridge artwork: bridged games are no longer grey tiles in Steam Game Mode. Cached SteamGridDB artwork is copied into your Steam grid directory after the shortcuts are applied — cover to capsule, background to hero, clear logo to logo. Original bytes are copied as-is with no resize, skips are reported, and nothing is ever written outside a detected Steam account directory.
- Information card: a "Time to beat" fact, validated
http:/https:fact values rendered as safe links, and a Links row that derives the Steam store and custom website URLs. - Fixed: the Big Box pause overlay trapped gamepad users — the pad kept driving the grid behind it, A launched the highlighted game, and B killed Big Box entirely. The overlay now owns the pad while it is open. Attract mode can no longer start over the pause panel or the Game Night overlay, and opening the pause overlay for a game with no documents no longer crashes. The three per-surface gamepad poll loops are unified into one
requestAnimationFrameloop instatic/gamepad.jsthat dispatches each frame to the highest-priority active surface, with no change to input behavior.
Full OpenBox 1.14.0 release notes · Compare v1.13.1...v1.14.0 · 1.11 API reference · 1.12 API reference
1.13.1 (2026-09-22) — Fixes
The Windows release had sharp edges that made several everyday paths dead ends. This is the sweep that removes them; no new features.
- Dialogs opened invisibly. The utility dialogs — prompt, choice, confirm, and the Trophy Case — were rendered inside a
hiddenwrapper, so every path prompt and every confirmation was a silent dead end, including the unsaved-changes guard on the game editor. Clicking outside a nested dialog also closed every open dialog rather than only the topmost, which could dismiss the whole setup wizard mid-flow. - Setup wizard: source tiles used emoji icons that render as tofu boxes without an emoji font (now monograms), a selected source was never highlighted, duplicates piled up with no way to remove one, completed steps were not clickable, "More sources" collapsed on every re-render, the scan-empty message told you to start a scan that had already run, the raw preview hash was shown on Confirm, and Finish offered actions that do nothing on an empty import. The wizard also re-opened on every library refresh while the library was empty; dismissing it now holds for the session.
- Library and navigation: the empty library hid "Start your library" below a full screen of empty insight widgets, so the insights panel now stays hidden until the library has games. Big Box on an empty view showed only a fleeting toast and now explains the prerequisite and opens the setup wizard. "View imported games" after a scan landed on an empty library because the batch id was stored but never projected into the payload. Setup wizard Readiness rows and Activity Center job titles showed raw candidate and job hashes instead of the detected title. "Scan all save paths" reported a bogus updated count and now points at the Activity Center, where the queued scan reports real progress.
- Editor and settings: saving with a required field on a hidden tab failed silently and now jumps to the invalid field. Switching tabs with a dirty form no longer prompts "Discard unsaved changes?" when nothing is discarded. Prev/Next navigation no longer appears while adding a new game. The Media tab showed about 22 path rows at once; the 16 rarely used media types are behind a "Show more media types" toggle that auto-expands when the game already has values there. "Hidden sidebar sections" is a checkbox group instead of a comma-separated free-text field, and hiding a section now hides its grid and not just its header. Play Insights no longer pushes the grid below the fold at short viewports, and the Activity badge no longer renders "0" before the job stream connects.
- Session and reliability: reloading the page dropped the session token and forced a fresh sign-in, so it is now kept in
sessionStoragefor the tab's lifetime. A malformed or rejected message on the native WebView2 bridge used to leave the frontend promise hanging forever; it now rejects so the UI can surface the error. When every server-sent-event subscriber slot was taken the server opened a stream that only ever sent heartbeats; it now answers503 SSE_BUSYwithRetry-After. Reconnecting leaked the previousEventSource, and a hidden tab could send state-changing requests with stale state and overwrite newer state written by the visible tab — mutations from hidden tabs are now blocked and close on hide. - Windows specifics: "Reveal in Explorer" never opened anything because the command line passed to
CreateProcessWhad noexplorer.exefirst token. A failed Start Menu or protocol registration left a half-registered installation behind; the installer now restores the previous installation tree. Static JS and CSS were servedimmutablefor a year, so app updates could run stale modules until the cache expired; app statics now sendno-cacheand rely on the ETag. - Time and locale: late-night moments were filed under the wrong day in the Story view and the history timeline, because timestamps were not converted to the viewer's local day before grouping. Six user-visible strings were never translated and now go through i18n in all five locales.
- Reliability: utility dialogs created lazily now share the same focus-restoration wiring as dialogs in the page markup, clicking outside a dialog routes through the shared close path, and every Setup Center open path goes through the single refresh-and-render entry point so reopening the wizard never shows stale content.
Full OpenBox 1.13.1 release notes · Compare v1.13.0...v1.13.1
1.13.0 (2026-09-19) — Windows
OpenBox runs natively on Windows x86_64 alongside Linux. The runtime is unchanged: standard library only (plus ctypes on Windows), no pip install, no requirements.txt, no virtualenv.
- Native Windows host:
native_host.exerenders the same UI over the loopback server through WebView2 and exposes the samewindow.openboxNativebridge the page already uses. It remembers window geometry, provides the tray icon and minimize-to-tray, handlesopenbox://deeplinks, enforces a single instance per data directory through a named pipe (a second launch focuses the running window), shuts the server down gracefully on close, and force-kills the process tree through a job object if it does not exit. The WebView2 runtime is present on Windows 11 and most Windows 10 systems. The release ships the host compiled: the portable zip carriesnative_host.exe, and the same binary is attached to the release on its own asOpenBox-x86_64-windows-native-host.exe(with.sha256and.sig) so a source checkout gets the native window without the MSVC toolchain.powershell -File scripts/build_native_host_windows.ps1still builds it fromnative_host_win.cnext toweb_app.pyfor anyone compiling it themselves (Visual Studio Build Tools with the C++ workload; WebView2 SDK from the NuGet cache or from nuget.org).OPENBOX_NATIVE_HOSToverrides the host binary path. Withoutnative_host.exethe launchers open the same UI in the browser app window, so Windows works out of the box. - Windows launchers:
openbox.cmdis the double-clickable wrapper,openbox.ps1runs the ladder (native window, else browser app window;--webforces the browser), andopenbox-native.ps1runs the native host or falls back. Python is located aspython.exeorpy.exeonPATH, or throughOPENBOX_PYTHON;OPENBOX_SHAREpoints the launchers at the runtime directory. - Verified Windows installer: the release asset
install.ps1(scripts/install.ps1) runs on Windows PowerShell 5.1 with the standard library only — it needs neither curl nor OpenSSL — and follows the same verification ladder as the Linux installer: it fetchesopenbox-release.pub, checks its SHA-256 against a pinned bootstrap anchor (unless-PublicKeyPathis given), then verifies the archive's.sha256sidecar and its Ed25519.sigbefore extracting. - Install layout:
OpenBox-x86_64-windows.zip(with its.sha256and.sig) is a portable zip of the full source tree under one top-levelOpenBox/folder.-InstallDir, orOPENBOX_INSTALL_DIR, defaults to%LOCALAPPDATA%\OpenBox; the runtime lands in<InstallDir>\share\openboxand the previous tree is kept at<InstallDir>\share\openbox.previousfor rollback. The installer registers a Start Menu shortcut and theopenbox://protocol handler, and adds the bin root to the user PATH unless-NoPathUpdateis given. - Windows data directory:
%LOCALAPPDATA%\openbox-game-launcher, overridable withOPENBOX_DATA_DIR; the layout matches Linux —library.json,server.token,server.port, media, backups, themes, and logs. - Verified Windows updates: the in-app updater checks the same key pin, SHA-256, and Ed25519 signature in pure Python (no OpenSSL), downloads
OpenBox-<arch>-windows.zip, and swaps the installed tree; the previous tree stays in place for rollback. - Windows emulator support: every bundled emulator definition carries its Windows executable name, so adapter detection, resume state, and Launch Doctor work with Windows builds (Dolphin, RetroArch, PCSX2, RPCS3, Cemu, melonDS, PPSSPP, Vita3K, xemu, Xenia, and the rest).
- Platform seam and CI:
pkg/platform_compat.pyholds the platform differences and uses only the standard library, plusctypeson Windows. A newwindows-latestCI job runs beside the Linux jobs.
Fixed
- Process liveness no longer probes with
os.kill(pid, 0)— that call terminates the target on Windows — so reattaching to or shutting down a running game no longer kills it. - Stored references use POSIX separators on Windows: SBOM symlink targets, emulator-directory token parents, and the runtime-module manifest.
- The metadata database closes cached SQLite handles before replacing the file, so a resync cannot fail with a locked database.
Still Linux only: gamescope and Game Mode, AppImage and Flatpak packaging, XDG desktop entries, sudo make install, Steam Deck and handheld tuning, and Flathub-aware emulator management. Linux remains the primary distro-integration target; Windows support is x86_64 only. See Windows for the platform guide.
Full OpenBox 1.13.0 release notes · Compare v1.12.1...v1.13.0
1.12.1 (2026-09-15) — Hardening
Small, safe update on top of Living Library: a harder update path, clearer errors, and no more silent skips.
- Verified updates: the AppImage updater rejects order-8 Ed25519 small-order keys, fails closed on non-object release payloads, normalizes the signature digest before validation, resolves a symlinked install destination so the real AppImage is replaced (with directory fsync), and escapes
%so paths cannot inject desktop-entry field codes. - Clearer errors: unreadable Steam libraries surface an
errors[]array naming the path and OS error; RetroAchievements 401/403 responses say the credentials were rejected; an unreachable metadata database reports a clean connection error instead of raw socket messages. - Reliability: the last manual rows are now gated tests —
docs/reliability.mdhas zero Manual rows — and coverage floors ratchet to 83% total / 58%web_app.py. - API fixes: deleting a missing playlist returns 404, scoped exports without a name fail fast with 400, Game Night queues explain empties with an
empty_reasonplus an exclusion breakdown, background jobs carry typed operation kinds (library.export,screenscraper.*,steamgrid.*,storefront.auto_import,clips.reel), and Backlog Radio rows hydrated from stored playlists carryestimated_minutes. - Test isolation: the suite no longer clobbers the developer library — an isolated
OPENBOX_DATA_DIRis exported for test runs and guarded at import time.
Full OpenBox 1.12.1 release notes · Compare v1.12.0...v1.12.1
1.12.0 (2026-09-14) — Living Library
Searches you pinned become shelves that stay correct on their own, every game gets a story worth scrolling, and the launch-options sheet is complete down to environment variables.
- Smart collections: "Save as collection" turns the active query-bar interpretation into a named sidebar shelf (
GET/POST /api/v2/collections,POST /api/v2/collections/delete). A collection stores the query — not a snapshot — so membership stays correct as the library changes. - Game Story: a per-game Story tab in the detail pane (
GET /api/v2/story?game_id=) narrates added date, first played, longest session, playtime milestones, progress, and captured Moments — a deterministic projection over the session journal, so it can never go stale. - Per-game environment overrides: Edit game → Launch gains a
launch_envfield (KEY=valuelines, validated at save) merged over the launch environment, plus an "Always confirm before launch" per-game flag. - Weekly automatic backups: Settings gains an opt-in weekly library backup with retention and a "last automatic backup" line; the scheduler reuses the existing backup engine (
auto_backup_due()). - SQLite self-enables for large libraries: at 5,000+ games the FTS read model turns itself on; an explicit
OPENBOX_ENABLE_SQLITE_READ=0opt-out is honored and small libraries see identical behavior. - Command palette learns: recently chosen games and actions rank first, from local usage counts — no telemetry.
- Fixed: the per-game gamescope preset override now round-trips to the Edit game select, plus the full 1.11.1 hardening sweep — complete large-media responses, document-reader framing, scroll-stable grid virtualization, and focus restoration.
Full OpenBox 1.12.0 release notes · Compare v1.11.0...v1.12.0 · 1.12 API reference
1.11.0 (2026-09-12) — Every Second Counts
Never lose your place
- Quick Resume stores progress-aware session state when the selected emulator adapter exposes a safe state path. Session recaps and a per-game Moments timeline keep recent activity, notes, screenshots, and resumable snapshots together. Resume is capability-based: adapters without a state definition still support notes and screenshots, but do not claim resume support.
- Record That captures the replay buffer through OBS when the optional local replay integration is enabled. If OBS is unavailable, it falls back to a local screenshot; if neither capture path is available, the action reports an error. Clips stay in approved local media directories, and bounded deterministic reels can be queued when the local tooling is present.
A library you can ask
- Backlog Radio builds up to five explainable recommendations from local play history and library data. It is deterministic and local; a new install without enough history uses a transparent fallback instead of inventing habits.
- The library query bar understands a small deterministic grammar such as
short unplayed rpg. Interpretation is shown as editable, removable chips; unknown words remain ordinary text rather than becoming guessed filters. - Ctrl/Cmd-K opens the command palette for games, actions, settings, and What's New discovery. The palette is a UI action, not a separate network service.
Time Machine
- The journal-backed Time Machine exposes a bounded event timeline, read-only as-of inspection, and reviewable revert previews. The canonical state remains transactional; malformed or stale plans stop before mutation. The journal records catalog metadata and history, not media binaries, so it is not a media restore mechanism.
Arcade Room and Household
- Arcade Room is a controller-friendly canvas showroom organized by platform. Museum mode turns it into a self-guided exhibit using real library facts, and reduced-motion settings use a static presentation.
- An optional salted Museum kiosk PIN is a local convenience boundary for browsing. It is not an account, network authentication, or security boundary.
- Household adds opt-in local members, challenges, results, shares, and leaderboard projections over the existing mounted sync folder. No account or hosted service is involved, and statistics remain off until a device opts in. Household is intentionally marked partial in the parity matrix because it does not provide hosted multiplayer.
Handheld migration and artwork
- Steam Bridge previews, applies, and removes OpenBox entries in Steam's
shortcuts.vdf, preserving unrelated records and rejecting stale reviewed plans.openbox --play <id>dispatches the same authenticated launch path for Steam Game Mode. - ES-DE imports
gamelist.xmlthrough a bounded, explicit-identity preview/apply workflow with source digests, stale-plan rejection, and transactional application. - The optional SteamGridDB provider can search, preview, apply, and bulk-match covers, backgrounds, clear logos, icons, and banners. It requires
STEAMGRIDDB_API_KEYand can be disabled in Settings; results are cached locally and provider media is constrained to HTTPS. - OpenBox launcher trophies are deterministic local awards evaluated from library and play-history data and persisted in a local trophy case. They are separate from the optional RetroAchievements account integration.
Polish
- What's New and tips discovery, localized UI coverage across the five stock locales, detail-pane Moments and Clips tabs, quick query chips, kiosk settings, and the final release polish sweep round out 1.11.0.
Full OpenBox 1.11.0 release notes · Compare v1.10.0...v1.11.0 · 1.11 API reference
1.10.0 (2026-09-08) — Safer migrations and synchronization
- Causal catalog sync: opt-in, content-addressed events carry device identity, ancestry, tombstones, recovery snapshots, and acknowledged outboxes. Preview incoming changes, resolve conflicts by field, and apply the reviewed plan transactionally; launch paths, commands, credentials, statistics, and media remain local.
- Legacy sync safety: the older whole-library
publish/pullprotocol now fails closed before mutation withLIBRARY_SYNC_UNAVAILABLE. Statistics sync remains available, while catalog publication requires the explicit v3 payload ({"protocol":"v3"}). - LaunchBox XML migration: preview discovered games, deduplication, emulator mappings, exclusions, and path decisions before applying a bounded migration transaction.
- Manual shelf entries: create, update, filter, export, and explicitly convert pathless entries for physical games and other catalog records without executable files.
- Large-library consistency: search, facets, health, exports, and shelf records use the same canonical state model; the optional SQLite read model accelerates indexed search while JSON remains the source of truth.
- Launch and packaging hardening: atomic launch reservations prevent duplicate starts, and release validation publishes signed x86_64 and aarch64 AppImages plus an x86_64 Flatpak bundle with checksums, zsync metadata, SBOMs, and installer tooling.
Full OpenBox 1.10.0 release notes · Compare v1.9.0...v1.10.0
1.9.0 (2026-09-04) — Look, Discover, Play
Mood Match — Adaptive Cover Theming
- New Settings → Appearance toggles "Adaptive cover theming" (
mood_match_enabled) and "Adaptive theming in Big Box" (mood_match_bigbox), off by default. The UI tints itself from a 5-color palette extracted live from the selected cover.
What Should I Play? (Picker)
- New picker replaces the random "Surprise me" button: pick by available time, mood (action/chill/story/retro/party), familiarity (new/favorite), and players. Top pick comes with an explanatory reason plus Launch / Details / Again actions (
POST /api/v2/library/pick).
Constellation
- New Tools → Constellation: full-screen, pan/zoomable relationship graph by series, developer, publisher, genre, platform family, and co-play history (
GET /api/v2/library/constellation).
Wrapped + Timeline + Mastery
- New Insights → Wrapped: printable "Your Year in Games" report (
GET /api/v2/insights/wrapped?year=YYYY); History → Timeline tab groups sessions by day. - New Tools → Mastery: completionist dashboard with per-platform/decade bars over local progress states plus a RetroAchievements column (
GET /api/v2/insights/mastery).
Game Night Party Mode
- New Big Box → Game Night: 2–8 players, couch-multiplayer queue, spinning wheel, persistent rounds, gamepad/keyboard control.
Library Migration & Sync
- LaunchBox XML migration: preview then apply (
POST /api/v2/import/launchbox/preview,/apply); emulator mappings are reported, never silently applied. - Full library sync (legacy design) via mounted folder with tombstones and last-writer-wins (
POST /api/v2/library/sync/publish,/pull). Superseded in 1.10.0: those legacy routes fail closed before mutation; use the reviewed causal catalog transport instead. - Manual shelf entries for games without local files (
POST /api/v2/library/manual-entry,manual_entry: true). - Big Box video snaps: looping gameplay videos in stage mode with debounce, BGM ducking, and reduced-motion support.
- SQLite read model graduation:
OPENBOX_ENABLE_SQLITE_READ=1now powers faceted search andGET /api/v2/library/search(FTS5 or LIKE fallback).
1.8.0 (2026-09-02)
Navigation & Routing
- Keyboard and gamepad navigation for the library grid and list view (arrows/Home/End/Page,
ffavorite, Escape clear, gamepad via configurable controller map with edge detection) (ADR 0021). - Hash routing: refresh and shared links restore platform/playlist/preset/query/selection/sort via
#/key/valuehash fragments (ADR 0021). - Sortable list-view columns with persisted direction (
list_sort/list_sort_dirsettings); screenshot lightbox with prev/next/zoom; cover skeleton loading shimmer.
ScreenScraper Provider
- Per-ROM-hash metadata and media scraping with credentials in
~/.env, 1 req/s throttle, 429/5xx backoff, 30-day disk cache, region-priority media selection, and an https-only URL guard (ADR 0022). - Additive v2 routes: status/test/search/info/match (batch, cancellable)/apply (durable job, downloads outside the state lock).
- Deliberately not wired into the LaunchBox match-review pipeline (ADR).
Custom Gamescope Presets
- User-defined presets (≤16, unique names, bounded ints) with per-game override; per-game
gamescope_presetwins over global (completes ADR 0016).
Library Export
- JSON/CSV export with
all/platform/playlistscopes, shareable-by-construction field projection, media paths opt-in, collision-safe filenames, and newest-10 rotation (ADR 0023). - Additive v2 routes: queue durable job, download with Content-Disposition, list exports.
ARM64 & Flathub Prep
- aarch64 AppImage release artifacts alongside x86_64; architecture-aware self-update refuses non-matching-arch releases (ADR 0024, un-defers ADR 0013).
- CI matrices build/attest/publish both arches (aarch64 on
ubuntu-24.04-arm). - Flatpak manifest runtime bumped
org.gnome.Platform 46→49; AppStream<content_rating>,<developer>, and<screenshots>added;docs/flathub-checklist.md(submission stays a maintainer decision).
Play Insights (Library)
- Play Insights now renders in the library with 30/90/365-day ranges, lazy IntersectionObserver load, debounced reload, and top-games deep links.
Fixed
gamescope_preset/mangohud_enabled/show_insightssettings persisted instead of silently dropped (M0 whitelist bug).- Context-menu "add to playlist" (
addGamesToPlaylist) and Big Box "Achievements" (openAchievements) no longer throw ReferenceErrors. - Chosen UI language now survives reload via
openbox-localelocalStorage. app:state-refreshedevent dispatched (debounced) fromlibrary.js refresh().- Changed-line coverage gate skips unmeasured test/script files (ADR 0025).
1.7.2 (2026-09-01)
Localization
- Full i18n system with
data-i18nattributes inindex.html,t(key)in JS viastatic/i18n.js, and JSON locale files for English, Spanish, German, French, and Portuguese inlocales/. - Settings → Interface language selector populated from
available_localesinpublic_settings; switching re-translates the UI without reload. scripts/check_i18n.pygate verifies 100% key coverage across all locale files; wired intomake check.
Scale Foundation
- Optional SQLite read model (
pkg/state/sqlite_readmodel.py) behindOPENBOX_ENABLE_SQLITE_READ=1env flag. Provides FTS5 full-text search (with LIKE fallback), indexed filtered queries, and GROUP BY facets. Disabled by default; zero behavior change when off.
Deck Polish
- Gamescope presets: 8 profiles (Steam Deck, HD, 1080p, 1440p, 4K, integer, stretch, borderless) in
pkg/parity/parity_gamescope.py; selectable in Settings → Controller. - MangoHud performance overlay toggle;
apply_mangohud_env()setsMANGOHUD=1on game launch when enabled. - Controller bench tab in Settings with live gamepad SVG visualization.
Emulator Health
- BIOS SHA1 drift detection in Launch Doctor: reports
BIOS_SHA1_DRIFTwhen a BIOS file exists but its hash doesn't match the expected value inemulator_defs/*.yaml. - Health badge CSS classes (ok/warn/fail) with tokens in
static/app.cssand all 5 themes.
Smart Collections & Backup Diff
- Visual chip builder for filter presets:
rules_to_chips()andchips_to_rules()inpkg/parity/parity_filter_presets.py. - Backup diff:
GET /api/v2/backup/diff?archive=<name>compares current library against a backup archive, returning added/removed/changed game IDs and settings change status.
Gates & Release
- New runtime module
pkg/state/sqlite_readmodel.pyadded toruntime_modules.txt(115 entries). 90% coverage (floor 85%). - 7 new GET routes (1 static + 5 locale JSONs + 1 backup diff v2).
- ADRs:
docs/adr/0014-sqlite-read-model.md,docs/adr/0015-i18n-system.md. - 15 new design tokens (6 gamepad + 9 health) across
static/app.cssand all 5 stock themes. Token baseline: 0.
1.7.1 (2026-08-29)
Play Insights
- Local-first Play Insights dashboard: 366-day heatmap (levels 0–4), current/longest streaks, top platforms/genres, and 30-day play momentum from local history with zero telemetry. Endpoints:
GET /api/v2/insights/summaryandGET /api/v2/insights/heatmap.
Performance
- Virtual spacer-window library grid with
localStorage['openbox-virtual-grid']kill-switch,IntersectionObserver+contain-intrinsic-size, and rAF coalescing for smooth 60 FPS scrolling with 20,000 games. - Off-main-thread search indexing and trigram lookups via
static/worker.search.jswith instant acronym search matching. pkg/state/cache.pyFacetCacheLRU (capacity 64) with epoch bumping on state changes;state_store.py50ms write coalescing with single fsync.
Setup & Launch Doctor Polish
- Setup Center
preview_documentincludes human-readable progress storytelling messages (preview_document.message). - Launch Doctor blocking checks carry structured, actionable
fix_actionobjects (flatpak_install,reveal_bios_path,pick_core,explain_token) with one-click fix buttons in the UI.
Frontend & Themes
- New design system tokens:
--overlay-insight-cell-0through--overlay-insight-cell-4,--border-insight,--shadow-insight,--surface-insight-card, and--focus-ring. All themes updated. - Lazy-loaded
static/insights.jsdashboard panel rendered above the library grid.
1.7.0 (2026-08-26)
Library Setup Center
- Guided Library Setup Center (
#setupLibraryButton) with preview-before-commit scan, paginated review, decision overrides, emulator readiness, and idempotent commit. - Side-effect-free scan previews with stale-preview guards and
import_batch_idtagging for instant post-import filtering.
Activity Center
- Durable Operation Service backed by
operations.json(replacing in-memory job maps) with queued, running, cancelling, done, partial, error, cancelled, and interrupted states. - Persistent topbar Activity button (
#activityButton), live SSE progress events, task cancellation, and restart recovery with retry/resume.
Launch Doctor
- Preflight validation for game paths, adapters, Flatpak/native executables, BIOS/firmware, and tokenized launch arguments before launching.
- Authoritative flat adapter-per-platform emulator registry (
emulator_defs/) with explicit launch precedence rules and ambiguity handling.
Additive v2 API
- Exact
/api/v2/*routes for setup preview/commit, emulator registry, launch preflight, metadata match review, and durable operations. - Canonical
library.jsonschema remains version 6; previews and operations use separate isolated storage files.
Packaging & Scale
- Formal performance support target of 20,000 games.
- AppImage release built on Ubuntu 22.04 LTS (x86_64); Flatpak targets runtime 25.08 (
org.gnome.Platform) — superseded: current builds target runtime 49 (see 1.8.0).
1.6.0 (2026-08-23)
Architecture & State Management
- State Decomposition: Decomposed
webapp_state.pyinto focused state modules:pkg/state/imports.py(import orchestration, duplicate merging, auto-import),pkg/state/commands.py(command execution), andpkg/state/registry.py(process and session tracking with typedSessiondataclass), keeping a lightweight backwards-compatible re-export facade. - Launch Token Centralization: Centralized launch token expansions (
{path},{name},{dir},{stem},{platform},{app_id},{heroic_app_id},{lutris_id},{rom_name},{DataDir}, etc.) intopkg/parity/launch_tokens.py. - Cache Invalidation Hierarchy: Consolidated caches and locks into coordinated
CacheEpochdataclass with atomic full invalidation (_invalidate_all()).
Frontend & Accessibility
- Accessible Tools Menu: Replaced details/summary tools dropdown with fully accessible WAI-ARIA button and menu pattern (
#toolsButton,#toolMenu, and#toolsWrap.open) supporting full keyboard navigation (Arrows, Home, End, Escape, Tab). - Memoized Search Index: Implemented LRU cache, debounced input, and bounded trigram expansion for instant lookups across 20k+ games.
- Dialog Focus Traps: Added dialog focus management with inert fallbacks and proper focus restoration on close.
- Virtual Grid Geometry: Memoized grid geometry calculation for faster library view rendering.
Security & Hardening
- Content Security Policy: Added
frame-ancestorsdirective to Content-Security-Policy (CSP) headers. - Narrowed Exception Handling: Hardened exception handling across handlers, state imports, commands, and SSE streams to eliminate broad except catches and add structured error logging.
- Auth & Input Validation: Added input validation and authentication checks to native dialog, window, and emulator scan endpoints.
- Write-Path Performance Benchmark: Added performance benchmark write-path gate (under 500ms for 10k games).
1.5.1 (2026-08-19)
Performance
- Optimized State Writes: Added dirty-field tracking, batched snapshot persistence, and cached library projections for large libraries.
- Import & Search Throughput: Accelerated import scanning, metadata batching, archive inspection throughput, and BigBox CoverFlow title indexing.
- Native Host Startup: Improved native host responsiveness with non-blocking IPC polling.
Changed
- Cross-Store Consolidation: Enhanced cross-store import consolidation across Steam, Heroic, Lutris, Faugus, and ROMs using canonical identity normalization.
- CLI & Argument Parsing: Hardened CLI help formatting and argument parsing for headless and native host invocations.
- Parity Documentation: Updated parity compatibility shims and LaunchBox feature matrix documentation.
Fixed
- Synchronous Future Cleanup: Ensured completed background job futures are released synchronously to eliminate memory and future retention.
- Import Validation: Hardened import endpoint error handling and input validation against malformed payload structures.
1.5.0 (2026-08-18)
Changed
- Proton & Wine Prefix Manager: Integrated Wine prefix and Proton discovery and assignment (
parity_wine.py,handlers/wine.py,GET /api/wine/prefixes,GET /api/wine/protons,GET /api/wine/prefix-for-game), with UI integration in game settings and auto-detection for Windows titles. - Faugus Launcher Integration: Added Faugus game discovery and import handlers (
parity_faugus.py,handlers/faugus.py,GET /api/faugus/status,GET /api/faugus/scan,POST /api/faugus/import) with automated UMU prefix detection. - Eden Nintendo Switch Emulator: Added emulator definitions and profile mapping for Eden (
emulator_defs/eden.yaml) supporting NSP, XCI, NCA, and NRO packages. - Canonical Identity & Cross-Source Deduplication: Added canonical identity resolver (
parity_identity.py,POST /api/health/dedupe) to deduplicate cross-source titles accurately across Steam, Heroic, Lutris, Faugus, and local ROM libraries. - Game Dialog Next & Previous Navigation: Added Previous (
← Prev) and Next (Next →) navigation buttons directly to the Game Edit modal, allowing users to rapidly cycle and edit adjacent games in the active filtered and sorted library without closing the dialog. - Platform-Scoped Media Cleanup: The media manager and
POST /api/media/cleanupnow accept an optionalplatformparameter, enabling duplicate media auditing and cleanup targeted to a single platform or across the entire collection. - Reset Play Statistics: Added a "Reset play statistics" action to the game right-click context menu and the Bulk Edit dialog, resetting
play_count(0),playtime_seconds(0), andlast_played(""). - Smart Capability Playlist Rules: Filter presets and smart filter playlists now support capability rules including
has_saves,has_achievements,has_missing_media, andhas_highscores. - Desktop Random Game Shortcut: Added
Ctrl+Alt+QandCtrl+Alt+Rglobal desktop keyboard shortcuts to instantly pick, focus, and scroll to a random game in the active grid or list. - Acronym Title Search Matching: Search and filter queries now recognize game title acronyms and initials (for example,
ootmatches The Legend of Zelda: Ocarina of Time,mgsmatches Metal Gear Solid,sotnmatches Castlevania: Symphony of the Night,ffmatches Final Fantasy). - Expanded Launch Variables: Emulator startup templates and per-game command overrides now expand
{ImagePath},{dir},{Dir},{file},{File},{stem},{FileNameWithoutExtension},{Platform},{EmulatorDir}, and{DataDir}. - Window Resolution CLI Options: Added
--fullscreen-width <W>,--fullscreen-height <H>, and--resolution <WxH>CLI flags to customize viewport dimensions for kiosk and app window modes.
Fixed
- Background job manager futures are now popped and cleaned up synchronously when worker jobs finish, preventing latent callback latency races in multi-threaded test and runtime environments.
Hardened
- Standardized all newly added parity integration modules under
pkg/parity/with root compatibility shims, adhering strictly to the frozen v1 contract.
1.4.0 (2026-08-17)
Fixed
- AppImage and Flatpak builds now bundle every static JavaScript module (
util.js,state.js,library.js, and related ES modules), preventing 404 errors during client-side navigation. - AppImage packaging scripts now ensure parent directories are created for
pkg/paritymodules. - Hardened background job manager queue slot cleanup on worker replacement, preventing in-flight job leakages.
- Handled non-standard filesystem mounts in
fsync_directoryand protected snapshot rotation against concurrent file removals. - Resolved Big Box mode switch exit response hang and ensured full controller dropdown navigation.
Changed
- Reorganized repository structure: documentation moved to
docs/, tests totests/, and parity integrations topkg/parity/with backward-compatible shims at root. - CI workflows, test runners, coverage gates, and token checkers updated to support both flat and packaged layouts.
1.3.0 (2026-08-16)
Hardened
- Plugin execution now uses bubblewrap with isolated namespaces, no network access, and temporary mounts for home, temporary, runtime, and removable-media paths when the host supports it. If the sandbox cannot be created, enabled plugins are skipped by default;
OPENBOX_ALLOW_UNSANDBOXED_PLUGINS=1is an explicit opt-in for trusted local plugins. - Gamescope regression tests now launch in their own process groups and terminate the full group on timeout, preventing nested gamescope processes from surviving the test gate.
Changed
- Cloud sync, save and backup restore, launch handling, settings validation, metadata application, Lutris import, 7z validation, webhook validation, filter matching, and game resolution were split into focused helpers, keeping the security-sensitive paths easier to audit and test.
- The background job manager now has dedicated coverage for retries, cancellation, queue and name limits, bounded results, shutdown, and completed-future cleanup.
- Dead backend code and obsolete browser, screenshot, migration, and performance-capture scripts were removed.
- From-source setup documentation now explains the tokenized UI URL and the supported
OPENBOX_ENV_FILE, data-directory, home-directory, and user-config locations. - CI and release tooling now use the refreshed GitHub Actions and JavaScript dependencies, with CodeQL action components kept on one version and grouped Dependabot updates for future changes.
1.2.0 (2026-08-15)
Security and SteamOS
- SteamOS AppImages no longer export bundled libraries into the host shell, fixing startup failures where
/bin/bashcould not resolverl_print_keybinding. - Webhook delivery rejects non-public destinations, pins validated DNS results, disables proxies and redirects, and bounds response reads.
- Library and save backups use private atomic files, reject unsafe archive entries, and protect restore paths against symlinks and archive replacement races.
- Native bridge authorization now requires the exact OpenBox origin, including its dynamic port.
- Gameyfin validates IDs before path construction, keeps filesystem operations under the install root, requires HTTPS, and verifies supplied checksums.
- 7z extraction rejects links, operates on a bounded snapshot, and validates the staging tree before promotion.
- Media and document reads enforce approved roots, including symlinked parent checks.
- Environment loading accepts only owner-controlled files and supported keys, and no longer searches the current directory.
- Job and SSE queues have explicit capacity, expiry, cleanup, and slow-client behavior.
Release hardening
- Release artifacts require Ed25519 signatures against the pinned production public key.
- Release jobs separate build, provenance attestation, and publication permissions, and refuse asset overwrites.
- Build and CI inputs are pinned, the SBOM is generated from the completed AppImage, and Puppeteer 25.7.0 resolves the audited npm dependency issues.
- Plugin catalogs require a pinned digest, HTTPS package URLs, and package checksums.
1.1.0 (2026-08-15)
Fixed
- Cloud sync: the local-wins merge branch contained a dead condition (
remote_played > local_playedcan never be true there), so newer per-field remote values were silently dropped. - State backup now mirrors the latest committed primary, staged atomically, instead of aliasing the brand-new write.
- Plugin environment filtering fixed a typo (
GAMEFYIN_->GAMEYFIN_), so correctly spelled Gameyfin variables are stripped from plugin subprocesses. - Play queue: skip flags recorded while advancing are written back to state before a valid item is returned, so they can no longer silently disappear.
- Queue
path_existschecks the filesystem instead of reporting any nonempty path string as existing. - OBS status:
recordingnow requires a recording file produced within the last two minutes instead of reporting any running OBS process as actively recording. - Steam and Lutris imports verify the Flatpak app is actually installed (
flatpak info) before building aflatpak runcommand. - IGDB: time-to-beat came from a nonexistent
time_to_beatfield on the games endpoint; it now queries the separategame_time_to_beatsendpoint and converts its seconds value to hours. - Gameyfin: the catalog requests the provider list once, raw responses are returned open and closed by the caller, and the tautological
str(folder) if installed else str(folder)is gone. - Job manager: completed futures are released via a done callback so job bookkeeping cannot accumulate indefinitely.
- Webhook retry: the injected clock now measures the sleep duration and warns when the wall-clock sleep overshoots.
- The emulator-defs YAML fallback parser no longer decides a key is a list based on whether its name ends in "s"; indented values build sequences from the actual shape.
- Native dialog bridge: selected paths are now JSON-quoted strings, so paths with spaces or quotes produce valid JSON and no longer leak the
g_strescapeallocation. - Ed25519 point decoding rejects out-of-range coordinates, small-order points, and off-curve values in both
updates.pyandscripts/verify_release.py, and checks the canonical scalar before point arithmetic. remove_exclusionreturns the number of removed entries; the API route reportsremovedtruthfully.sanitize_settingsno longer iterates a non-dict input into garbage dropped-key lists.- The 7z archive validator counts a final member even when the listing omits the trailing blank separator.
- Screenshot capture: the previously ignored
window_hintnow selects active-window flags for gnome-screenshot, spectacle, and scrot.
Changed
_tdp_argsreturns only the argument list; the unused milliwatt value is gone.
1.0.1 (2026-08-15)
Fixed
- Big Box hybrid mode: platform buttons were emitting a broken
data-bigbox-AppState.platformattribute that the click handler never matched, so switching platforms did nothing. The attribute now matches the selector. - IGDB search sent a malformed
&AppState.platform =query parameter instead ofplatform=, dropping the platform hint from searches. - Custom-field keys in the details pane were rendered without escaping; a crafted key could inject HTML. Keys and values are now both escaped.
- The session token stayed in the browser address bar after load. It is now scrubbed from history immediately, with deeplink parameters preserved.
Changed
- CSP tightened:
script-src 'self'without'unsafe-inline', plusobject-src 'none'andbase-uri 'none'. - Requests without a Host header are now rejected instead of bypassing the loopback check.
- The SSE stream now carries the same security headers as every other response.
- The startup URL printed to stdout no longer contains the session token.
- The updater verifies an Ed25519 signature when a release publishes one, and skips with a loud warning while the public key is still the placeholder.
- WebKit rendering defaults changed: dmabuf renderer disabled unless
OPENBOX_ENABLE_DMABUFis set (fixes silent window failures on AMD GPUs, including Steam Deck), and hardware acceleration switched to on-demand.
Hardened
- The native host now validates full URIs (scheme, host, no userinfo, no control characters) before handing anything to the default handler.
- Reveal-in-folder is restricted to paths under the data directory or home directory.
- The native bridge rejects suspicious payloads instead of evaluating them.
- Plugin catalog downloads now require a valid sha256 checksum; entries without one are refused.
- Plugin subprocess environments are scrubbed of token, password, secret, and API-key variables.
before_launchplugin hooks can no longer swap the launch binary or move the working directory outside the game or data directories; tampered results fall back to the original command.
Fixed (launch reliability)
- AppImage launches now route through the fallback ladder instead of exec-ing the native host directly, and all launch failures are written to
~/.local/share/openbox-game-launcher/openbox-launch.loginstead of vanishing on a double-click. - The native host writes its own log and the single-instance message is no longer invisible.
- Game Mode with no kiosk browser installed now prints the server URL instead of failing silently.
Verification
./run_all_tests.sh: 47 test files, 0 failures at that tag; the then-current suite had 62 test files.make check: lint, compile checks, coverage floors green; then-current floors were60%total and48%web_app.pyinscripts/check_tests.py(now83.0%/58.0%).- CI smoke test now covers Big Box platform switching and IGDB search parameters; JS linting runs in CI; Dependabot watches GitHub Actions and npm.
1.0.0 (2026-08-14)
Native-first
- OpenBox now opens in a native WebKitGTK window by default, rendering the same library UI as the web app instead of a browser tab or the removed Tk interface. The C host (
native_host.c) owns server lifecycle, single instance, window geometry, minimize-to-tray, and a fallback ladder to the system-browser app window (then your default browser) when WebKitGTK is missing;openbox --webremains the development opt-out. - The
ui_windowapp/browser split is removed; the Tk interface no longer ships, and AppImage dependencies drop python3-tk/tcl/tk for WebKitGTK 4.1. CI compiles the native host on every pull request. - Native IPC:
/api/native/*routes report host capabilities dynamically, and a JS↔C bridge drives native dialogs, external opens, reveal-in-file-manager, and Big Box fullscreen. - Batch metadata auto-match binds every unmatched game whose title exactly matches the LaunchBox Games Database in one action, instead of matching one game at a time. Only exact normalized-title hits qualify; ambiguous titles are left unmatched.
State and API contract
- Schema v5 adds a host-owned
ui_stateblock; existing games, settings, playlists, and history migrate untouched. - The v1 API contract freezes (
contracts.py+v1_contracts.json, 61 routes) with a CI check that fails when the contract drifts.
Frontend
- The topbar regroups into Library, Actions, and Tools zones; session and job events stream over Server-Sent Events with polling kept as a fallback.
- Grid covers group by aspect ratio by default (persisted as
cover_grouping), and the dialog manager traps focus and closes on Escape. - Scroll-lag fixes: rAF-coalesced grid rendering, backdrop blur removed from the base scroll path, hover-gated cover transitions, and a constrained workspace row.
Under the hood
- The 260-method
Handlerclass splits into capability mixins underhandlers/(library, media, imports, sessions, settings, extensions, health, emulators, native, data);web_app.pydrops from 3,755 to 1,628 lines while response bytes and route wiring stay identical. docs/adr/0001-native-host.mdanddocs/native-host-contract.mddocument the host/server split and IPC contract.
Verification
- 47 test files, 0 failures at that tag;
make checkgates passed with the then-current floors. At that tag the suite had 62 test files with60%total coverage floor and48%web_app.pyfloor perscripts/check_tests.py(now83.0%/58.0%). The UI smoke test drives a real server and asserts the Tools menu opens under every stock theme.
0.9.0 (2026-08-12)
LaunchBox media catalog and archive manuals
- Media downloads now cover box backs, box spines, 3D boxes, clear logos, fanart, banners, title screens, cart fronts, cart backs, discs, and advertisement flyers, beyond covers, backgrounds, and screenshots. Every media surface (metadata dialog, bulk download, media audit, artwork gallery, image groups, auto-import) accepts the expanded set.
- Manuals are not in the LaunchBox feed, so the manual option pulls a PDF or text manual out of the game's own archive, ranking
manual.pdffirst and reporting a "no manual in this archive" note when nothing is found. - Platform name mapping ranks exact LaunchBox matches first:
Game BoytoNintendo Game Boy,PlayStationtoSony Playstation,GameCubetoNintendo GameCube,XboxtoMicrosoft Xbox, across 26 aliases.
Engineering foundation
make checkruns lint, compile checks, the full test suite under coverage, and coverage floors in one command. CI enforces it on push, pull requests, and weekly, and a version-sync check fails whenupdates.pydisagrees with any published version spot.- The 613-line GET and 195-line POST dispatch chains became a route registry (
routes.py) with 104 GET and 124 POST entries (including v1 aliases, on top of 80 GET and 95 POST base routes), each mapped to a named handler (five via dottedhandlers.native.*specs). - Structured errors carry stable machine codes (
GAME_NOT_FOUND,MEDIA_JOB_RUNNING, ...) plus a per-request id that appears in the UI and the diagnostic log; POST validation errors become400 BAD_REQUESTinstead of leaking to the generic 500 path. - A versioned
/api/v1surface aliases the stable routes; legacy paths keep working. - The library payload is gzip-compressed once per state change and served with conditional GET: 5,000 games serve in about 2 ms at 638 KB instead of 13.8 MB.
- Settings saves drop unknown keys against a 72-key whitelist instead of persisting junk. (The registry in
settings_schema.pycurrently defines 72 known keys.)
Reliability
- Rolling state snapshots keep the last 5 committed states for point-in-time recovery;
/api/state/recovergained dry-run preview and snapshot restore modes. - Background jobs keep a 50-entry finished history at
/api/jobs, rendered in the Library Audit dialog. - Ctrl-C / SIGTERM stops running sessions and drains webhooks gracefully; the session poll drops to every 10 s when idle.
- Log redaction now covers RetroAchievements keys and
client_secretshapes, and/api/diagnosticpackages a redacted report for bug reports.
Frontend
index.htmlshrank from 3,117 lines to a 485-line shell; JS and CSS serve from/static/*with cache headers.- All 30 browser state globals moved into one
AppStateobject; UI preferences persist throughlocalStorage. - Server errors surface in a dismissible banner with a "Copy details" action that includes the request id.
- Dialogs are
aria-modal, toasts and lifecycle messages are live regions, and the label floor rose to 12px. - The interface language selector is honestly English-only until real localization lands.
- A UI smoke test drives a real server with puppeteer and fails on page errors.
Release and supply chain
- CycloneDX 1.4 SBOM generation, Ed25519 release signing with a pure-stdlib verifier, and a release pipeline script that runs everything up to the human publish step.
- A 23-scenario reliability catalog (
docs/reliability.md), a support matrix (SUPPORT.md), and a triage policy (TRIAGE.md).
Fixed
- Duplicate media cleanup now scans every media field, not just covers and backgrounds.
- Latent undefined names in
web_app.pythat would have crashed their code paths on first use. - Lint debt across the gate rule set: default-argument calls, loop-variable closures, unused variables, lambda assignments, missing
check=onsubprocess.run, shebangs, and import placement.
Verification
- Ran
./run_all_tests.sh: 45 test files, 0 failures. - Ran
make check: lint, compile, tests, and coverage gates pass (56% total, 44%web_app.py). - UI smoke test boots a real server and drives the grid with no page errors.
- Perf bench at 5,000 games: gzip library 1.9ms / 638KB vs 13.7ms / 13.8MB plain.
0.8.2 (2026-08-12)
Fixed
- Box art now keeps its natural aspect ratio in the library grid, Big Box Stage, and CoverFlow views instead of being force-cropped into a single ratio, so games with non-standard artwork (for example SNES titles) display uncropped. Title-only covers keep the standard portrait box.
Added
- Web UI surfaces for the persistent play queue, normalized game tags, Notification Center, and signed webhook settings.
/api/queue,/api/tags,/api/notifications, and/api/webhookscontracts with bounded state, secret redaction, and destination validation.
0.8.1 (2026-08-09)
Fixed
- Launching a game with no launch command and no matching platform profile now fails with a clear error before anything runs, instead of silently spawning a process that exits on the spot and reporting a normal session end. Games whose file is not executable get the same message.
- The web UI shows the real outcome of a failed session. An immediate exit with a non-zero code reports "Session failed" with the exit code and a hint to check the launch command and emulator install, instead of the generic "Play time and history were saved" toast.
- Emulator installs no longer re-add the Flathub remote when it already exists, which previously made
flatpak remote-add --userexit non-zero and abort the install.
0.8.0 (2026-08-07)
Added
- Handheld performance profiles: per-launch-profile TDP limits applied via
ryzenadjat launch, with an optional restore limit when the session ends, gated by anApply handheld performance limitssetting (auto / always / off).autoapplies only on Steam Deck / Bazzite game mode and battery-powered handhelds; a missingryzenadjor permission error logs a warning and never blocks a launch.
0.7.0 (2026-08-02)
Fixed
- Backup restore merges archived settings back into
library.jsoninstead of a sidecar file the app never reads. - Restoring an older backup over a newer library is refused unless explicitly forced, and restore re-validates symlink parents after
mkdir. - Save backups resolve relative
save_pathsagainst the game instead of the process working directory, and reject symlinked backup directories. - Cloud sync propagates local deletions and resolves per-game conflicts by
last_played. - The plugin
libraryhook is TTL-cached so/api/libraryno longer blocks up to 5 seconds per plugin per request. openbox://deeplinks reject foreign hosts and fail clearly when no server port is known.- Gamescope guest detection also recognizes
STEAM_GAMESCOPE_RESTRICTEDsessions.
0.6.0 (2026-07-30)
Added
- LaunchBox-style advanced search in the Web UI: field terms, quoted values, status filters, and negative terms.
- Ordered manual playlists with parent grouping, notes, membership editing, and keyboard-friendly reorder controls, alongside existing filter playlists.
- Game context actions, Ctrl/Shift multi-selection, configurable status badges, richer platform/category/playlist detail panes, related-game reasons, artwork galleries, and per-game launch profile overrides.
- Backup archive listing, manifest summaries, restore actions, expanded artwork groups, and metadata fields for controller support, disc count, portable games, and broken entries.
Changed
- State persistence uses schema migrations, stable game identities with legacy aliases, last-known-good recovery, process-safe transactions, atomic writes, and corruption preservation.
0.5.0 (2026-07-30)
Added
- Steam Game Mode guest support for Steam Deck, Bazzite, and similar gamescope sessions. Big Box opens fullscreen while Steam retains Input, Quick Access Menu, and TDP controls.
- Settings can remove all Steam-imported library entries at once, keeping game files and media on disk.
- Local rotating diagnostic logs with a Settings copy button. Tokens and passwords are redacted.
0.4.x (2026-07-24 to 2026-07-30)
- 0.4.8, Steam Game Mode guest:
--game-modeopens Big Box fullscreen under gamescope on Steam Deck, Bazzite, and similar handheld images. Guest sessions are detected automatically, and Steam keeps Input, QAM, and TDP while OpenBox tags its UI and non-Steam launches for Steam's overlay path. - 0.4.9, library reliability: session playtime and Gameyfin installs update the correct library entry after deletes or reorders, Gameyfin downloads stage then replace and stream to disk, Lutris CLI failures return JSON errors.
- 0.4.10, AppImage desktop launches: via Gear Lever and similar integrators no longer leak bundled
LD_LIBRARY_PATHinto hostxdg-open/browsers.
0.1.0 to 0.3.0 (2026-07-23 to 2026-07-24)
The first public builds: the local web UI and native Tk interface, folder and storefront imports, emulator profiles, metadata and media downloads, session history, save discovery and backups, Big Box layouts, themes, plugins, and the token-authenticated REST API.