chore(deps): update ghcr.io/rommapp/romm docker tag to v5 #91

Open
renovate-bot wants to merge 1 commit from renovate/ghcr.io-rommapp-romm-5.x into main
Member

This PR contains the following updates:

Package Update Change
ghcr.io/rommapp/romm major 4.4.1 → 5.4.1

Release Notes

rommapp/romm (ghcr.io/rommapp/romm)

v5.4.1

Compare Source

[!CAUTION]
This release includes mitigations for the recent eMudhra CA compromise as well as general dependency upgrades. While we don't believe RomM instances are vulnerable, we recommend upgrading as soon as possible.

What's Changed

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/5.4.0...5.4.1

v5.4.0

Compare Source

[!CAUTION]
Give the first start 15-20 mintes to finish its migrations.
Several of this release's migrations rewrite the roms table and backfill it in batches, which can take a while on a large library. Don't restart the container while they run.

[!WARNING]
Cross-origin requests are denied by default.
An unset or empty ROMM_CORS_ALLOWED_ORIGINS used to allow every origin, and now allows none. If a browser-based client on another origin talks to your RomM, list its origin in ROMM_CORS_ALLOWED_ORIGINS. A * still answers any origin, but without credentials, so a client that signs in with the session cookie needs its origin listed explicitly. #​4699

[!WARNING]
Add a reverse proxy on a public address to FORWARDED_ALLOW_IPS.
RomM now trusts X-Forwarded-For only from loopback and private ranges. A proxy outside those ranges has to be added, or every request is logged and rate limited as coming from the proxy's address. #​4748

Technical changes

🌎 ENVIRONMENT VARIABLES
Notifications and email
variable default description
SMTP_HOST - SMTP server; email stays off until this and SMTP_FROM are set
SMTP_PORT 587 SMTP server port
SMTP_USERNAME - Login for the SMTP server, if it needs one
SMTP_PASSWORD - Password for the SMTP server
SMTP_FROM - Sender address, such as romm@example.com
SMTP_SECURITY starttls starttls, tls (implicit TLS, usually port 465) or none
Audit log
variable default description
AUDIT_LOG_RETENTION_DAYS 90 Days to keep audit events; 0 keeps them forever
Library conversion
variable default description
ROM_CONVERTO_ENABLED false Enable the "Convert library" task and ?format= download conversions
ROM_CONVERTO_TIMEOUT 600 Seconds per rom-converto operation
ROM_CONVERTO_MAX_CONCURRENCY 2 Concurrent conversions per web or task worker
Devices and sync
variable default description
DEVICE_INSTALL_ENABLED true Let users push a ROM to one of their devices for install
DEVICE_INSTALL_REQUEST_TTL_DAYS 2 Days an unfinished install request waits for its device; 0 waits forever
DEVICE_INSTALL_EXCLUDED_PLATFORM_SLUGS win,win3x,win9x,windows-apps Platforms that can never be pushed to a device
SYNC_RETROARCH_PSP_SERIAL_MAP {} JSON map of PSP serial to extensionless ROM file name, for RetroArch PSP saves whose title matches no ROM
Scheduled cleanups
variable default description
ENABLE_SCHEDULED_CLEANUP_NETPLAY true Clean up empty netplay rooms
SCHEDULED_CLEANUP_NETPLAY_CRON */30 * * * * When that cleanup runs
ENABLE_SCHEDULED_CLEANUP_UPLOAD_TMP true Clean up abandoned chunked uploads
SCHEDULED_CLEANUP_UPLOAD_TMP_CRON 0 * * * * When that cleanup runs
ENABLE_SCHEDULED_CLEANUP_ZIP_CACHE true Clean up stale cached ZIP files
SCHEDULED_CLEANUP_ZIP_CACHE_CRON 0 4 * * * When that cleanup runs
ENABLE_SCHEDULED_CLEANUP_SYNC_SESSIONS true Fail sync sessions no client completed
SCHEDULED_CLEANUP_SYNC_SESSIONS_CRON 23 * * * * When that cleanup runs
General
variable default description
⚠️ ROMM_CORS_ALLOWED_ORIGINS - Empty now allows no cross-origin requests; a * allows any origin but never credentials
⚠️ FORWARDED_ALLOW_IPS local/loopback addresses Proxies trusted to report the client's address; was *
DISABLE_EASYRPG false Disable the EasyRPG player for everyone
SEVEN_ZIP_TIMEOUT 180 Timeout for 7-Zip operations in seconds, was 60
📡 API CHANGES | Change | Description | | --- | --- | | ⚠️ CORS denied by default | `ROMM_CORS_ALLOWED_ORIGINS` now defaults to empty instead of `*`; browser clients on another origin must be allowlisted (#​4699) | | CORS wildcard drops credentials | With `*` in `ROMM_CORS_ALLOWED_ORIGINS`, responses no longer send `Access-Control-Allow-Credentials` (#​4699) | | Invalid credentials are unauthenticated | A malformed Basic header or an invalid/expired JWT bearer no longer 500s; the request proceeds unauthenticated and gets the route's 401/403 (#​4799) | | ⚠️ `PermissionGroupSchema.is_system` removed | Replaced by `system_key` (`SystemGroupKey` or null) (#​4767) | | Age limits on permissions | `PermissionGroupSchema/Create/Update` and `UserPermissionsSchema/Update` gain `age_limit`, `hide_unrated_roms`; updates apply them only with `set_age_settings: true` (#​5081) | | Collection `rom_count` is computed | `rom_count` on collection, smart and virtual collection schemas is now read-only, derived from the visible `rom_ids` (#​4930) | | ⚠️ Collection name errors | Duplicate name on create or rename returns 409 (was 500); names over 400 chars return 422 (#​4997) | | ⚠️ `GET /api/netplay/list` | Scope `assets.read` - `roms.read`; `game_id` is now an integer ROM id, 404 for a missing or hidden ROM (#​4700) | | ⚠️ Netplay socket rooms | `open-room` and password-less `join-room` require a signed-in user with `roms.read` who can see the ROM; the room password moved from `extra.room_password` to top-level `password` (#​4700) | | ⚠️ `GET /api/play-sessions` | Without `devices.read`, only allowed when `device_id` equals the calling token's device; no longer defaults `device_id` from the token (#​4956) | | ⚠️ `PlaySessionSchema.sync_session_id` removed | Field dropped from play session responses (#​4670) | | Page params on play and sync sessions | `GET /api/play-sessions` and `GET /api/sync/sessions` use shared page params (`limit` 1 to 10000, `offset`); sync sessions gains `offset` (#​4715) | | ⚠️ Sync `delete` operation | `SyncOperationSchema.action` adds `delete` (slot emptied on the server); `SyncNegotiateResponse` adds `total_delete` (#​4674) | | `POST /api/sync/negotiate` payload | New `restore_unlisted` and `emulators` fields to re-offer unlisted saves and filter by emulator (#​5091) | | Save sync baselines | `POST /api/saves` and `PUT /api/saves/{id}` take a `content_hash` query param; `POST /api/saves/{id}/downloaded` takes `content_hash` in the body (#​4768) | | Save/state uploads validated | `emulator` must be a single folder name (max length enforced); file names over 255 bytes return 400; uploads to a hidden ROM return 404 (#​4851, #​4997) | | `SaveSchema` / `StateSchema` annotations | New `is_favorite` and `labels`; masked to defaults on another user's shared save or state (#​4714) | | `StateSchema.core` | New field naming the RetroArch core that wrote the state (#​4862) | | ⚠️ `PUT /api/roms/{id}` rename errors | Renaming onto an existing file returns 409 (was 500); an invalid or overlong `fs_name` returns 400 before any metadata fetch (#​4997) | | `PUT /api/roms/{id}` manual metadata | `raw_manual_metadata` is validated, 422 when malformed; `ManualMetadata` gains `alternative_names` (#​5111) | | `POST /api/roms/{id}/convert-to-folder` | Also returns 409 when an `.m3u` beside the file lists it; upload and walkthrough routes reject the same case (#​5076) | | `GET /api/roms` relevance ordering | Empty `order_by` with a search term ranks by relevance on every database; `char_index` is empty under relevance order (#​4996) | | `GET/HEAD /api/roms/{id}/content/{file_name}` | New `?format=` for converted single-file downloads (202 + `Retry-After` while converting, 406 when unavailable) (#​4999) | | `GET /api/roms/{id}/content/{file_name}` | New `purpose=download\|play` query param, recorded in the audit log (#​4748) | | `DetailedRomSchema.download_formats` | Formats the caller can request with `?format=` (#​5000) | | `is_easyrpg_game` on ROM schemas | New boolean on `SimpleRomSchema` and `DetailedRomSchema` (#​5077) | | Pinned media | `RomUserSchema.pinned_media` and `RomUserData.pinned_media` (ordered media keys, null for default) (#​4764) | | ROM metadata fields | LaunchBox box art `box2d_*`/`box3d_*` (url and path), `video_path` now nullable (#​5129); SS `physical_disc`/`physical_extra_discs` (#​4885); SS and Hasheous `dump_regions/languages/tags` (#​4740); RA `hash_match` (#​4816) | | `PlatformSchema` | New computed `abbreviation` and `alternative_names` (#​5107) | | `MusicTrackSchema.file_name` | New field (#​5079) | | `DeviceSchema.capabilities` | New `capabilities` map on device schema and on `POST`/`PUT /api/devices` payloads (#​4835) | | `GET /api/heartbeat` | Adds `EMULATION.DISABLE_EASYRPG` (#​5077), `NOTIFICATIONS` (#​4735), `DEVICE_INSTALL` (#​4835), `CONVERTO` (#​4999) | | `GET /api/config` | Adds `CONVERTO` and `CONVERTO_LIBRARY_TARGETS` (#​4999) | | `POST /api/tasks/run/{task_name}` | Returns 409 when a single-instance task is already queued or running; `TaskInfo` adds `destructive` (#​5000) | | `POST /api/reset-password` | Enforces the password policy with 400, leaving the reset link usable (#​5106) | | OIDC callback | A provider error redirects to `/login?bypass_autologin=true` instead of 500 (#​4746); an account linked to a different provider identity gets 403 (#​4892) | | `POST /api/activity/heartbeat` | Returns 404 for a ROM hidden from the caller (#​5117) | | ⚠️ `activity:update` socket event | Sent only to users who can see the ROM, instead of broadcast to every client (#​5121) | | ⚠️ `scan` socket event payload | Validated strictly (`ScanPayload`, unknown keys rejected); invalid options, no scan worker, or a scan in flight emit `scan:done_ko` (#​5014) | | `scan:scanning_rom` | Payload adds `is_new` (#​4867) | | `sync:conflict` | Payload adds `rom_name` (#​4552) | | Socket session revocation | Logging out or revoking a session disconnects the sockets it opened (#​4732) | | Streaming session routes | Control routes take `container` and `claimed_at` query params; launch socket payloads add `claimed_at`, `core`, `core_tier`, import `refusals`; `StreamingContainerSchema` adds `supports_live_states`, `import_kinds`, `state_core`; 409 bodies use `ContainerBusyDetail` (#​4631, #​4691, #​4796, #​4859, #​4862) |
Notifications
Method Path Description
GET /api/notifications List the caller's notifications, newest first (#​4732)
POST /api/notifications Send a notification to yourself, or to other users with users.write (#​4732)
POST /api/notifications/read Mark the given notifications (or all) read (#​4732)
DELETE /api/notifications/{notification_id} Dismiss one notification (#​4732)
DELETE /api/notifications Dismiss all notifications (#​4732)
Notification channels
Method Path Description
GET /api/notification-channels List the caller's channels (Apprise, webhook, email) (#​4735)
POST /api/notification-channels Create a channel; email addresses get a confirmation code (#​4735)
PATCH /api/notification-channels/{channel_id} Update a channel (#​4735)
DELETE /api/notification-channels/{channel_id} Delete a channel (#​4735)
POST /api/notification-channels/{channel_id}/test Send a test notification now (#​4735)
POST /api/notification-channels/{channel_id}/confirm Confirm an email channel with its code (#​4735)
POST /api/notification-channels/{channel_id}/resend-code Resend the email confirmation code (#​4735)
GET /api/notification-channels/apprise-services List Apprise services and their fields (admin) (#​4769)
POST /api/notification-channels/apprise-services/parse Parse a service or Apprise URL into fields (admin) (#​4769)
Audit events
Method Path Description
GET /api/audit-events Paginated audit log, filterable by actor, action, category, target, time range and search; admins with users.read see everyone (#​4748)
Device installs
Method Path Description
GET /api/devices/online IDs of the caller's devices with an open /devices socket (#​4835)
POST /api/devices/{device_id}/installs Queue a ROM install on a device (#​4835)
GET /api/devices/{device_id}/installs List the device's pending and taken install requests (#​4835)
POST /api/devices/{device_id}/installs/claim Device claims its pending requests (device-bound token only) (#​4835)
PUT /api/devices/{device_id}/installs/{request_id} Device reports done, already_installed or failed (#​4835)
DELETE /api/devices/{device_id}/installs/{request_id} Cancel a pending or taken request (#​4835)
GET /api/roms/{id}/installs The caller's open install requests for a ROM (#​4835)
RetroArch Cloud Sync (WebDAV)
Method Path Description
OPTIONS /api/sync/retroarch/{path} Advertise DAV support (#​3904)
PROPFIND /api/sync/retroarch/{path} Read-only browsing of roms/, saves/ and states/ (#​3904)
GET, HEAD /api/sync/retroarch/{path} Serve the manifest or a save/state file (#​3904)
PUT /api/sync/retroarch/{path} Upload a save/state, matched to a ROM by file name (#​3904)
DELETE, MOVE /api/sync/retroarch/{path} Remove a save/state the client dropped (#​3904)
MKCOL, LOCK, UNLOCK /api/sync/retroarch/{path} Accepted no-ops for WebDAV clients; uses HTTP Basic with a 401 challenge (#​3904)
Saves and states
Method Path Description
PUT /api/saves/{id}/favorite Set is_favorite on a save (#​4714)
PUT /api/saves/{id}/labels Replace a save's labels (#​4714)
PUT /api/saves/{id}/file-name Rename a save file and its screenshot (#​4749)
PUT /api/states/{id}/favorite Set is_favorite on a state (#​4714)
PUT /api/states/{id}/labels Replace a state's labels (#​4714)
PUT /api/states/{id}/file-name Rename a state file and its screenshot (#​4749)
Collections
Method Path Description
PUT /api/collections/{id}/visibility Share or unshare a collection (owner only) (#​4749)
PUT /api/collections/smart/{id}/visibility Share or unshare a smart collection (owner only) (#​4749)
ROMs, tasks and config
Method Path Description
POST /api/tasks/scan Queue a library scan with a ScanPayload body; 202, 409 if one is in flight, 503 with no worker (#​5014)
GET /api/roms/{id}/easyrpg/{path} Serve the EasyRPG index.json and game/RTP files for RPG Maker 2000/2003 games (#​5077)
PUT /api/config/converto_settings Update download conversion settings (#​4999)
Socket.IO events
Method Path Description
Server to client notifications:new / notifications:read / notifications:dismissed Notification created, marked read, or dismissed for the user (#​4732)
Server to client install:updated An install request changed, sent to the owner's tabs (#​4835)
Server to client install:queued / install:cancelled Sent to a device on the new /devices namespace (device-bound client token required) (#​4835)
Server to client activity:refresh Signals clients to refetch the activity list (#​5125)

Highlights

Notifications

RomM now keeps a notification inbox for each user. Scans and tasks that finish or fail, a role change, or someone ending your stream all leave a notification that is pushed to every open tab and still waits for you the next time you open RomM. Admins can send their own from the Send tab on the Notifications page, and any API client can post one through POST /api/notifications. #​4732

Each user can also forward their notifications to channels, filtered by level and topic. A RomM webhook sends a JSON payload (signed with HMAC-SHA256 when you give it a secret), and email works once the SMTP_* variables are set, which also sends password reset links by email. Admins can additionally pick any of the services Apprise supports, such as Discord, Telegram, Slack, ntfy, Gotify or Matrix, and fill in that service's fields or paste its URL. #​4735 #​4769

Screenshot 2026-10-06 at 6 52 34 AM
RetroArch Cloud Sync

RomM is now a Cloud Sync target for RetroArch. Point RetroArch's Cloud Sync at https://<your-romm>/api/sync/retroarch/ (the trailing slash matters) and sign in with your RomM username and password (setup guide). Saves and states sync straight into your library, web player states show up in RetroArch's numbered load slots, and RetroArch registers itself as one of your devices on its first sync. #​3904

The config, thumbnails and system file categories sync too, stored per user. PSP save folders are bundled into one save per folder; if a folder's title matches no ROM, map its serial with SYNC_RETROARCH_PSP_SERIAL_MAP. Generic WebDAV clients can browse the same path read-only. #​3931

Browser saves sync, and installs on your devices

All five in-browser players (EmulatorJS, EasyRPG, js-dos, PICO-8 and Ruffle) now sync saves through device sync. Each browser profile registers as a device, named like "Firefox on macOS", and negotiates its saves with the server when a game launches, so a save made in one browser is waiting in another and a conflicting copy is archived instead of overwritten. A new Devices page at /devices lets you rename your devices, turn sync off for one, or remove it. #​5091

You can also send a game to a device from the web UI. "Install on device" in a game's menu adds it to the download queue of the devices you pick, and the device downloads it the next time it's online. Only devices whose app reports it accepts installs are listed. Turn the feature off with DEVICE_INSTALL_ENABLED=false, and keep platforms out of it with DEVICE_INSTALL_EXCLUDED_PLATFORM_SLUGS. #​4835

Screenshot 2026-10-06 at 6 53 03 AM
Parental controls

Give a permission group an age limit, and its members only see games rated for that age or younger. Each user can replace their group's limit, and a "Hide unrated games" switch hides games no rating covers. Admins are never limited. Set them in the group and user dialogs under Administration. #​5081 #​5083

A game's age comes from the strictest of its IGDB, ScreenScraper, LaunchBox (ESRB) and Steam ratings, and a manual rating list replaces the providers' ratings. The limit applies everywhere a game can show up: the gallery, search, collections, stats, the Jukebox and feeds. See Parental controls for the details.

Screenshot 2026-10-06 at 6 53 46 AM
Audit log

RomM now records who did what: downloads and player launches, play sessions, uploads and edits, collection changes, scans and tasks, and security events like sign-ins, failed sign-ins and permission changes. Admins read it in the new Events tab under Settings › Logs, filtered by user, category and date, with a search over names and IP addresses. Events are kept for 90 days, set by AUDIT_LOG_RETENTION_DAYS (0 keeps them forever). See Audit log for everything that's recorded. #​4748

Screenshot 2026-10-06 at 6 54 34 AM
Library conversion with rom-converto

Pick a storage format per platform, and the new "Convert library" task converts every matched game on it in place, so a game keeps its saves and collections. Only lossless conversions are offered, and because the task deletes the originals it asks you to type a confirmation first. Set ROM_CONVERTO_ENABLED=true and choose formats under the new Conversion settings page, or in config.yml (setup guide). #​4999

With download_conversion_enabled on, the game page's More menu offers "Download as" in any format the file can be converted to, and clients can ask for one with ?format= on the download URL. #​5000

converto:
  download_conversion_enabled: true
  platform_formats:
    psx: chd
    ngc: rvz
RPG Maker 2000/2003 in the browser

RPG Maker 2000 and 2003 games on the rpg-maker platform now play in the browser on the EasyRPG web player. The free EasyRPG RTP ships with it, so games that rely on the RTP start, though some assets (mostly in battles) are missing from it. Games need to be extracted folders, since the web player can't read archives. Disable it with DISABLE_EASYRPG. #​5077

Screenshot 2026-10-06 at 6 56 37 AM

Search now matches the alternative titles your metadata providers know about, including localized names, so a Japanese title finds the game listed under its English name and the other way around. You can add your own in the new "Alternative titles" field of the Edit dialog, such as "ACNH" or a fan translation's name. Already matched IGDB and Hasheous games pick up their regional titles on their next metadata refresh. #​5111

The Search page also orders results by relevance until you pick a sort, with exact title matches first. #​4995 #​4996

Minor changes

Fixes

Other changes

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/5.3.1...5.4.0

v5.3.1

Compare Source

Minor changes

Fixes

Other changes

Full Changelog: https://github.com/rommapp/romm/compare/5.3.0...5.3.1

v5.3.0

Compare Source

[!CAUTION]

Breaking changes

Filesystem structure must be explicitly declared

We no longer detect the filesystem layout by default, and an instance will refuses to start until explicitly declared in your config.yml. This is the equivalent of structure A:

filesystem:
  structure:
    default: "roms/{platform}/{game}"
    firmware: "bios/{platform}"

And the equivalent of structure B:

filesystem:
  structure:
    default: "{platform}/roms/{game}"
    firmware: "{platform}/bios"

filesystem.roms_folder and filesystem.firmware_folder have also been removed. An instance that still sets either will refuse to start and print the equivalent template.

See Custom library structure and the filesystem.structure schema.

[!WARNING]
Emulator streaming config is now one entry per container, not one per emulator.
A container's host, protocol and label become the defaults for every platform it serves, and a platform block only overrides what differs. Existing streaming setups need their config.yml rewritten before the feature will come back up.

See Emulator Streaming for the new schema.

[!NOTE]

Environment variables

Emulation

variable default description
MAX_SAVES_PER_SLOT 50 Versions kept per save slot, newest first (0 disables)
STREAMING_LAUNCH_TIMEOUT 600 Seconds to wait for a streamed emulator to launch
STREAMING_STATE_HISTORY_LIMIT 50 Save states kept per ROM, emulator and user

Physical games

variable default description
UPC_LOOKUP_ENABLED true Barcode lookups when adding a physical game
UPC_LOOKUP_API_KEY - Key for the UPC lookup service
UPC_LOOKUP_URL https://api.upcitemdb.com/prod/trial/lookup UPC lookup endpoint

Recommendations

variable default description
ENABLE_SCHEDULED_BUILD_RECOMMENDATIONS true Nightly rebuild of the recommendation index
SCHEDULED_BUILD_RECOMMENDATIONS_CRON 30 5 * * * When that rebuild runs

Browser players

variable default description
DISABLE_JSDOS false Disable the js-dos player for everyone
DISABLE_PICO8 false Disable the PICO-8 player for everyone

Metadata providers

variable default description
DEMOZOO_API_ENABLED false Enable the Demozoo metadata provider
POUET_API_ENABLED false Enable the Pouët metadata provider
CSDB_API_ENABLED false Enable the CSDb metadata provider
STEAM_API_ENABLED false Enable the Steam storefront metadata provider
HASHEOUS_API_URL https://hasheous.org/api/v1 Use a self-hosted Hasheous instance

General

variable default description
SCAN_WORKERS 4 Parallel scan workers, was 1
WEB_SERVER_CONCURRENCY 4 API server workers, was 1
DB_POOL_RECYCLE_SECONDS 300 Recycle pooled DB connections after this many seconds

Highlights

Custom library structure

Instead of guessing between two folder structures, describe your exact library layout in config.yml. {platform} is the platform folder, the final {game} is where a game starts, and anything else in {braces} is just a folder you use to organize things. You can override the layout per platform, and give a platform a list of layouts when its games sit at more than one depth. #​3571

ROM identity is now content-hashed, so you can move files around and keep their saves, favorites, play history and collections. And if you came from Batocera, RetroBat or ES-DE, their platform folder names are recognized out of the box, so most libraries need no mapping at all. #​4345

filesystem:
  structure:
    default: "roms/{platform}/{game}"
    firmware: "bios/{platform}"
    ps3: "roms/{platform}/{category}/{game}"
    snes: "roms/{platform}/{region}/{game}"
    nes:
      - "roms/{platform}/{game}"
      - "roms/{platform}/{category}/{game}"

Docs: Custom library structure, Folder name aliases

Emulator streaming

A massive update to emulator streaming. You configure one entry per container instead of one per emulator, a platform can pool several containers and grab whichever one is free, and platforms that do both browser and streamed play now get separate Play and Stream buttons. #​4314

Saves and save states made while streaming are pulled back into your library with history and thumbnails. Multi-disc games get a disc-swap control, and whichever disc was mounted when you saved is the one remounted when you load. PS2 and GameCube can sync a whole memory card instead of individual save files, with a memory card library to create, rename, version, share and download them. Admins get a desktop session for setting up an emulator with no game running, plus a fleet view to force-release sessions across every container.

Docs: Emulator Streaming

Physical games

Own a game on a cartridge or disc, but have no ROM for it? You can now add it anyway, by name or by scanning its barcode with your camera, and it gets matched to metadata like any other game. #​3848

Docs: Physical Games

Screenshot 2026-09-11 at 6 00 19 PM
Walkthroughs

Walkthroughs are now documents on a game, sitting next to its manuals. Upload your own, or paste a GameFAQs URL and have it fetched directly (as plain text). It'll even remember where you left off and show a progress bar for each user. #​3816

Docs: Walkthroughs

Screenshot 2026-09-11 at 6 03 47 PM
Title IDs read from the binary

Scans now pull a game's native Title ID straight out of its binary on PSX, PS2, PS3, PSP, PS Vita, Switch, 3DS, Wii, Wii U, GameCube, Dreamcast, Xbox and Xbox 360. It identifies games on the platforms we don't hash, so a renamed or moved file keeps its saves and collections, and it tells the system where a game writes its saves, so device sync knows what to look for. #​3925

Docs: Title ids read from the binary

Jukebox

A soundtrack player for your whole library that follows you as a mini player anywhere in the app. Browse by album, platform, artist or genre, or just put a playlist on: Free Radio picks a randomized, album-balanced hour, and there's a Decade Mix, recently added, and your personal favorites. #​4197

Docs: Jukebox

Screenshot 2026-09-11 at 6 05 57 PM
JS-DOS and PICO-8 in the browser

Windows 3.x and 9x games now run on js-dos and its DOSBox-X backend. Note that js-dos saves stay in your browser and are not synced back to the server. #​4061

PICO-8 cartridges (.p8 and .p8.png) are also now playable, on FAKE-08, with keyboard + mouse, gamepad, or touch input. #​4400

Docs: js-dos, PICO-8

Screenshot 2026-09-11 at 6 06 41 PM
Four new metadata sources

Steam is now a metadata source for the win, linux and mac platforms, no API key required! It's in the scan, in the Match ROM dialog when a PC game was missed or matched to the wrong app, and in the cover picker alongside SteamGridDB. #​4241 #​4498 #​4500

Demozoo, Pouët and CSDb cover demoscene productions on platforms we already support. Tag a file (demozoo-108), or paste an id or a production URL into the ROM editor. #​4221

scan:
  priority:
    metadata:
      - steam # PC platforms only

Docs: Metadata Providers

EmulatorJS cores and multi-disc games

A multi-disc game is handed to EmulatorJS all at once now, so you can swap discs from the emulator's own menu instead of going back and reloading. Set emulatorjs.disable_batch_bootup to boot only the disc you launched, if a core mishandles the batch. #​3988

You can also preselect a core per platform with emulatorjs.default_cores. That sets the starting point, and anyone who has already picked a core on their device keeps their choice. #​4452

Docs: Cores, Multi-disc games

Library-aware recommendations

Recommendations are now built from your own library, blending metadata overlap, the list pulled from IGDB, and what you actually play. You'll find them under Similar games on a game's page, and in a new Recommended for you row on the home screen. Enable it by setting ENABLE_SCHEDULED_BUILD_RECOMMENDATIONS=true in your env vars. #​4174

Docs: Recommendations

Screenshot 2026-09-11 at 6 08 32 PM
Saves

A save syncs automatically with the server seconds after its stored in the browser. The player watches the emulator's SRAM while you play and uploads a new version as soon as one appears, so your progress is saved even if the tab is closed or the browser crashes. #​4378 #​4558

Saves are also organized into slots, the same model used by the sync clients. Ordinary play goes to the autosave slot, which keeps a capped history; a named slot on launch keeps every version. Each version carries a screenshot from the moment it was written, and the server prunes a slot to MAX_SAVES_PER_SLOT (50 by default). #​4517 #​4540

In-game, the Export and Import save buttons are replaced by a single Load save or state picker that restarts the game from the save you pick. #​4600 #​4615

emulatorjs:
  auto_save_sync: false # opt ou & Quit

Docs: Saves & States

Sort and filter by game length

You can now sort and filter the gallery by how long a game takes, straight from the gallery rather than having to build a collection for it. It's based on the HowLongToBeat main story time, so it needs HLTB_API_ENABLED and a scan that matched the game. #​4392

Docs: Game length

Minor changes

Fixes

Other changes

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/5.2.0...5.3.0

v5.2.0

Compare Source

Minor changes

Fixes

Other changes

[!NOTE]

API changes

Change Description
GET /roms/random Retrieve one rom picked at random, or null when the scope holds none. Accepts platform_ids, collection_id, virtual_collection_id and smart_collection_id.
GET /roms Now accepts a with_total parameter (default true). Set it to false when the caller already knows the total; total then comes back null unless the rom id index is being built and already carries it.
GET /setup/library existing_platforms now comes back empty once the database holds roms, instead of walking every platform directory.
POST /states Now rejects an upload against a rom the caller cannot see.
DELETE /roms/{rom_id}/files/{file_id} Now requires the ROMS/DELETE grant instead of ROMS/WRITE.
GET /feeds/fpkgi Package names are deduplicated within a rom, so two packages that share a title id no longer overwrite each other on the console.
GET /collections/smart/{id} The cached rom_ids, rom_count and covers are now resolved as the collection owner, not the requesting viewer. The live rom listing is still per-requester.
CustomLimitOffsetPage[SimpleRomSchema] total is now nullable.
InviteLinkSchema Adds url, the full registration link built from ROMM_BASE_URL, or null when that points at loopback.
MetadataSourcesDict Adds SS_DEV_CREDENTIALS_SET.
RomSSMetadata Adds box2d_path.
RomGamelistMetadata Adds box2d_back_path, fanart_path and title_screen_path.
RomHasheousMetadata Adds mame_redump_match.

Streaming endpoints

These moved from ROMS_READ to ROMS_USER_WRITE, which is always-on for authenticated users but absent from the read-only set a kiosk visitor gets.

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/5.1.0...5.2.0

v5.1.0

Compare Source

[!WARNING]
When binary logging is on and the connecting user lacks SUPER, trigger DDL is refused unless the global log_bin_trust_function_creators flag is 1. Run this on your MariaDB as an admin/root user (not the romm app user):

SET GLOBAL log_bin_trust_function_creators = 1;

SET GLOBAL is lost on DB restart. Either add to your MariaDB my.cnf under [mysqld]:

log_bin_trust_function_creators = 1

Or grant the app user the privilege (MariaDB 10.5+):

`GRANT BINLOG ADMIN ON *.* TO 'romm'@&#8203;'%';`   -- or: GRANT SUPER ON *.* TO 'romm'@&#8203;'%';

[!WARNING]
If your reverse proxy caches responses itself, or overrides/strips Cache-Control, it will defeat our built-in cache and serve a stale index.html that points at bundles the new image no longer ships, which usually shows up as a blank page or a broken UI after upgrading. After upgrading, purge your proxy's or CDN's existing cache once so clients stop being served entries stored under the old (header-less) behaviour.

Highlights

Emulator streaming

Launches a game into a native emulator running in a separate container and streams the picture, sound, and input back to your browser. Unlike EmulatorJS, the emulation runs server-side on real emulator binaries (PCSX2, Dolphin, Xemu), so the heavy lifting happens on the host rather than in the client. #​3211

[!IMPORTANT]
Emulator streaming is in early development, and documentation is coming soon! If you really want to try it today, refer to this pull request in the rommapp/docs repo.

image2
Per-field artwork priority overrides

Optional per-field artwork priority to prioritize metadata sources differently for covers, screenshots, and manuals, which fall back to the shared scan.priority.artwork. #​3838

scan:
  priority:
    cover: # Cover art only
      - igdb
      - ss
    screenshot: # Screenshots only
      - ss
      - igdb
    manual: # Game manuals only
      - launchbox
    region_mode: prefer_rom_tags  Region tags win over priority list
Scan settings UI

Brand new UI to edit the whole scan.* section of config.yml. #​3854

Screenshot 2026-07-21 at 8 20 20 PM
Manual cover search filters

Client-side filters to the manual cover-search dialog to quickly narrow SteamGridDB results instead of scrolling through dozens of community covers. #​3857

Screenshot 2026-07-21 at 8 20 44 PM

Minor changes

[!NOTE]

Environment variables

variable default description
PLAYMATCH_API_URL https://playmatch.retrorealm.dev/api/v2 Use self-hosted instance of Playmatch
STREAMING_BROKER_SECRET - Required for emulator streaming
STREAMING_SAVE_TIMEOUT 45 Save request timeout (in seconds)
ROMM_CORS_ALLOWED_ORIGINS Comma-separated list of allowed CORS origins (empty allows all)
ROMM_SESSION_SECURE_COOKIE 'false Mark session and CSRF cookies Secure (enable when served over HTTPS)
MAX_ASSET_UPLOAD_SIZE_BYTES 536870912 Max size of a save/state/screenshot upload request in bytes (0 disables the limit)
MAX_AUTOCLEANUP_LIMIT 100 Max number of saves a client can keep per slot when autocleanup is on (minimum 1)

Fixes

Other changes

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/5.0.0...5.1.0

v5.0.0

Compare Source

romm-5 0-banner

Highlights

UI overhaul

A ground-up redesign of the UI, with a new design system, fresh visual language, and a universal input model that feels at home with mouse, touch, keyboard and gamepad alike. With a stronger emphasis on performance, responsiveness, ease-of-use and accessibility, the new UI should be a joy on any device. #​3436

Screenshot 2026-07-11 at 2 02 26 PM
Screenshot 2026-07-11 at 2 01 49 PM
Screenshot 2026-07-11 at 3 06 19 PM
Shared savestates

Share your game saves and savestates with other users; useful for speedrunning, special unlocks, new game +, or just showing off your progress. #​3569

Screenshot 2026-07-12 at 2 43 57 PM
Interactive 3D boxart

Boxart go weeeeeeeeeeeeeeeeeeeeeeeeeeeee! #​3580

Jul-11-2026 14-55-17
CRT mode

Take me back to the 90s with a CRT shader that simulates the look of those old displays we love dearly. #​3552

Screenshot 2026-07-12 at 2 47 24 PM
Server-side ROM patching

We've unlocked the full power of ROM patching by moving it to the server. Simply apply patches to your ROMs, now with the option to use stored or uploaded patch files. #​3225

Screenshot 2026-07-12 at 2 50 04 PM
QR code pairing

Quickly pair your PC, TV or handheld device with your server by scanning a QR code (currently available on Argosy Launcher and Playnite). #​3308

Screenshot 2026-07-13 at 8 26 06 AM
Real-time logs

Tired of SSHing into your server to tail the logs when something breaks? Now you can view the backend logs in real-time directly from the web UI. #​3557

Screenshot 2026-07-12 at 2 42 01 PM
Controller debug view

Test your controller inputs and mappings in real-time with a new debug view. #​3581

Screenshot 2026-07-13 at 8 23 47 AM
Changelog preview

A quick peak at the changes in the current version, with links to the full changelog. #​3560

Screenshot 2026-07-12 at 2 48 01 PM
Granular permission system

Tweak access to your library with per-user and per-group permissions, with one configurable default group for new users. Tri-state grant/revoke on top of the group, and per-entity overrides, allow for fine-grained control over who can see what. #​3605

Screenshot 2026-07-13 at 8 22 43 AM
Music player

Music files stored within a ROMs' soundtrack folder can now be played directly from details page (#​3610), and queried directly from the API (#​3640). This opens the door to future features like background music playback and soundtrack playlists.

Screenshot 2026-07-13 at 6 56 42 AM

Minor changes

Fixes

Other changes

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/4.9.2...5.0.0

v4.9.2

Compare Source

What's Changed

Full Changelog: https://github.com/rommapp/romm/compare/4.9.1...4.9.2

v4.9.1

Compare Source

What's Changed

Full Changelog: https://github.com/rommapp/romm/compare/4.9.0...4.9.1

v4.9.0

Compare Source

[!WARNING]
The structure of scan exports in config.yml has changed! If you're currently exporting gamelist.xml files with scan.export_gamelist, migrate to scan.export.gamelist_xml: true

Highlights

Save sync engine

"Coming soon" no more! Third-party app developers will now be able to rely on RomM to keep save files in sync across devices and platforms. #​3137

Devices (apps, handhelds, browsers) can choose to register themselves with the server, which uniquely identifies them with a token. The system tracks which device has the latest version of a save file, flags conflicts when two devices change the same save, and syncs in whichever way fits the device: on demand, via a watched folder, or through a scheduled background sync.

Play session tracking

Another one for the app developers: client devices submit batches of play sessions per device, recording when and how long games were played. Sessions track both the time window (start_time/end_time) and screen-on time (duration_ms), which may differ due to device suspension or back-grounding. #​3155

Libretro thumbnails

The libretro thumbnail repository is now available as a first-class artwork source for region-correct box art (PAL/Europe, Japan, etc.). Matching is exact case-insensitive against the directory listing, so a ROM named "(Europe)" lands on the (Europe) artwork. The source is enabled by default, and you can set it's priority in your config.yml via the scan.priority.artwork list. #​3252

Pegasus metadata export

You can now auto-export a Pegasus compatible metadata.pegasus.txt file, just like the existing gamelist.xml export. Enable it in your config.yml file by setting scan.export.pegasus to true, and it should appear in each platform folder (alongside your ROMs). #​3153

Notable changes

Fixes

Other changes

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/4.8.1...4.9.0-beta.1

v4.8.1

Compare Source

What's Changed

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/4.8.0...4.8.1

v4.8.0

Compare Source

Highlights

Client API tokens

API tokens are a safer way for apps and devices to connect without sharing user credentials. A new QR-based pairing flow makes setup quick, with support for regeneration and revocation when needed. #​3114

Screenshot 2026-03-13 at 9 15 48 PM Screenshot 2026-03-13 at 9 25 26 PM
Chunked uploads

Goodbye failed uploads and incessant timeouts! ROMs will now upload in smaller chunks, allowing for more reliable transfers even on spotty connections. #​3076

Screenshot 2026-03-13 at 9 17 24 PM
Local LaunchBox metadata import

Have a local LaunchBox installation packed with games? You can now import metadata directly from it, including cover art, screenshots and video previews. #​2922

Screenshot 2026-03-13 at 9 26 29 PM

Minor changes

Fixes

Other changes

[!NOTE]
Environment Variables

UPLOAD_TIMEOUT has been removed in favour of chunked uploads
OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS: Access token lifetime (in seconds, default 30 minutes)
OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS: Refresh token lifetime (in seconds, default 7 days)
INVITE_TOKEN_EXPIRY_SECONDS: Invite token lifetime (in seconds, default 10 minutes)
OIDC_SERVER_METADATA_URL: URL to the OIDC provider metadata endpoint
OIDC_RP_INITIATED_LOGOUT: Enable RP-initiated logout flow (default false)
OIDC_END_SESSION_ENDPOINT: OIDC end-session endpoint override URL

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/4.7.0...4.8.0

v4.7.0

Compare Source

Highlights

Auto-export gamelist.xml

Automatically generate an ES-DE compatible gamelist.xml file during scans by enabling the option in your config.yml. This will place the new gamelist.xml file within the platform folder for selected/discovered platforms.

scan:
    export_gamelist: true

@​ItzCobaltboy in #​2987

Device-based save synchronization

Thanks to @​tmgast, we've laid the ground work for device-based save synchronization to enable multi-device save management, as defined in RFC-0001. Devices (handhelds, PCs, etc.) can register with the server and track which saves they've synced, enabling conflict detection when a device tries to upload stale data.

Note that this is purely foundational work to set up an initial structure and API implementation and in intended for third-party developers. #​2917

Minor changes

  • Add support for ScummVM shortnames by @​gantoine in #​2957
  • Add OIDC_AUTOLOGIN=true env var to automatically login using OIDC by @​pR0Ps in #​2969
  • Enable keyboard lock in emulatorjs by @​matthewturk in #​3043
  • Group games with same name (but different tags) as versions/siblings by @​zeroSteiner in #​2984
  • Search by mutliple keywords with | by @​gantoine in #​2996
    • zelda|mario|sonic would show all games with zelda, mario OR sonic in the name
  • New PKGJ feed endpoints in .tsv format by @​gantoine in #​2986
    • PSP games: GET /api/feeds/pkgj/psp/games
    • PSP DLC files: GET /api/feeds/pkgj/psp/dlc
    • PS Vita games: GET /api/feeds/pkgj/psvita/games
    • PS Vita DLC files: GET /api/feeds/pkgj/psvita/dlc
    • PSX games: GET /api/feeds/pkgj/psx/games

Fixes

Other changes

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/4.6.1...4.7.0

v4.6.1

Compare Source

What's Changed

Full Changelog: https://github.com/rommapp/romm/compare/4.6.0...4.6.1

v4.6.0

Compare Source

Highlights

Additional metadata

The edit dialog now allows you to manually set metadata properties that would otherwise only be aggregated from metadata sources, if those metadata sources don't support/populate certain fields. #​2848

Screenshot 2026-01-12 at 1 16 34 PM
Enhance folder structure config

We've improved the web interface that lets you configure platform bindings and versions, and excluded files and folders. This change should simplify the process of configuring how library folders are interpreted and what content is ignored during scanning. #​2809

Screenshot 2026-01-12 at 12 25 11 PM Screenshot 2026-01-12 at 12 25 23 PM
Patcher.js
  • We're introducing a new ROM patching feature to the app that enables your admins and editors to apply various types of patches to ROM files. At this time both ROM and patch files must be downloaded locally and re-uploaded via the patcher, with future work planned to patch ROMs directly from your library. #​2787
Screenshot 2026-01-12 at 12 25 37 PM
UI settings sync

UI settings are now synced between devices for every user! These settings do not affect third-party apps, and any setting must be changed at least once for this sync to take effect. #​2777

BIOS file defaults

A default BIOS file can now be specified per emulator core directly in your config.yml:

emulatorjs:
  settings:
    mgba:
      bios_file: gba_bios.bin

#​2873

Minor changes

Fixes

Other changes

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/4.5.0...4.6.0

v4.5.0

Compare Source

Highlights

Netplay

The incredibly smart and beautiful people over at the EmulatorJS project have added Netplay to their client, allowing you to play with your friends on the same instance over the internet! Enable netplay and configure the STUN/TURN servers in your config.yml:

emulatorjs:
  netplay:
    enabled: true
    ice_servers:
      - urls: "stun:stun.relay.metered.ca:80"
      - urls: "turn:global.relay.metered.ca:80"
        username: "<username>"
        credential: "<password>"

To host a game, start it, then hit the 🌐 icon in botton bar. Set your name, create a room (password optional), and other players should be able to see and join your room. All players need access to your RomM server to join a room and play together. #​2728

Multiple notes

You've begged for it, and boy did we ever delivered! Write and store multiple notes for each game, with full mark down support; sharable, swapable, and easily identified with the 📝 icon. #​2665

Screenshot 2025-12-10 at 3 52 36 PM

Metadata locale selection for IGDB and ScreenScraper

You can now set the order of your preferred locales/regional variants when fetching metadata from IGDB and ScreenScraper. The options below are available and can be set in your config.yml. #​2700

scan:
  priority:
    region:
      - "us" # United States (no localization needed)
      - "wor" # World (default)
      - "ss" # Screenscraper (default)
      - "eu" # Europe
      - "jp"  # Japan
      - "kr" # Korea
      - "cn" # China (simplified)
      - "tw" # Taiwan (traditional)
516566290-2c43d67e-f55e-400b-8a89-a23e25d1ef36

Minor changes

Fixes

Behind-the-scenes

New Contributors

Full Changelog: https://github.com/rommapp/romm/compare/4.4.1...4.5.0


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Mend Renovate.

This PR contains the following updates: | Package | Update | Change | |---|---|---| | [ghcr.io/rommapp/romm](https://github.com/rommapp/romm) | major | `4.4.1` → `5.4.1` | --- ### Release Notes <details> <summary>rommapp/romm (ghcr.io/rommapp/romm)</summary> ### [`v5.4.1`](https://github.com/rommapp/romm/releases/tag/5.4.1) [Compare Source](https://github.com/rommapp/romm/compare/5.4.0...5.4.1) > \[!CAUTION] > This release includes mitigations for the recent [eMudhra CA compromise](https://emudhra.we-have-a-problem.com/) as well as general dependency upgrades. While we don't believe RomM instances are vulnerable, we recommend upgrading as soon as possible. #### What's Changed - docs(readme): move corporate sponsors above friends by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5219](https://github.com/rommapp/romm/pull/5219) - chore(deps-dev): bump handlebars from 4.7.9 to 4.7.10 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5221](https://github.com/rommapp/romm/pull/5221) - fix(deps): bump certifi to 2026.10.19 to distrust compromised eMudhra intermediates by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5283](https://github.com/rommapp/romm/pull/5283) - chore(deps): bump uv to 0.13.4 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5284](https://github.com/rommapp/romm/pull/5284) - chore(trunk): Upgrade trunk by [@&#8203;github-actions](https://github.com/github-actions)\[bot] in [#&#8203;5225](https://github.com/rommapp/romm/pull/5225) - chore(deps): update github actions by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;5244](https://github.com/rommapp/romm/pull/5244) - chore(deps): bump aiohttp from 3.14.3 to 3.14.4 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5276](https://github.com/rommapp/romm/pull/5276) - chore(deps-dev): bump vite from 8.3.1 to 8.3.4 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5285](https://github.com/rommapp/romm/pull/5285) - chore(deps): bump follow-redirects from 1.16.0 to 1.16.1 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5286](https://github.com/rommapp/romm/pull/5286) - docs: list the ROM patcher env vars in env.template by [@&#8203;pcbeingused333](https://github.com/pcbeingused333) in [#&#8203;5265](https://github.com/rommapp/romm/pull/5265) - chore(deps): update dependency pillow to v13 \[security] by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;5288](https://github.com/rommapp/romm/pull/5288) - chore(deps): update dependency storybook to v10.6.2 \[security] by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;5291](https://github.com/rommapp/romm/pull/5291) - chore(deps): bump Node to 24.22.1 for the October 2026 security release by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5295](https://github.com/rommapp/romm/pull/5295) - fix(docker): upgrade the image's pip to 26.2.2 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5298](https://github.com/rommapp/romm/pull/5298) - chore(deps): update github/codeql-action action to v4.39.0 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;5299](https://github.com/rommapp/romm/pull/5299) #### New Contributors - [@&#8203;pcbeingused333](https://github.com/pcbeingused333) made their first contribution in [#&#8203;5265](https://github.com/rommapp/romm/pull/5265) **Full Changelog**: <https://github.com/rommapp/romm/compare/5.4.0...5.4.1> ### [`v5.4.0`](https://github.com/rommapp/romm/releases/tag/5.4.0) [Compare Source](https://github.com/rommapp/romm/compare/5.3.1...5.4.0) > \[!CAUTION] > **Give the first start 15-20 mintes to finish its migrations.** > Several of this release's migrations rewrite the `roms` table and backfill it in batches, which can take a while on a large library. Don't restart the container while they run. > \[!WARNING] > **Cross-origin requests are denied by default.** > An unset or empty `ROMM_CORS_ALLOWED_ORIGINS` used to allow every origin, and now allows none. If a browser-based client on another origin talks to your RomM, list its origin in `ROMM_CORS_ALLOWED_ORIGINS`. A `*` still answers any origin, but without credentials, so a client that signs in with the session cookie needs its origin listed explicitly. [#&#8203;4699](https://github.com/rommapp/romm/issues/4699) > \[!WARNING] > **Add a reverse proxy on a public address to `FORWARDED_ALLOW_IPS`.** > RomM now trusts `X-Forwarded-For` only from loopback and private ranges. A proxy outside those ranges has to be added, or every request is logged and rate limited as coming from the proxy's address. [#&#8203;4748](https://github.com/rommapp/romm/issues/4748) #### Technical changes <details> <summary>🌎 ENVIRONMENT VARIABLES</summary> ##### Notifications and email | variable | default | description | | -------------- | ---------- | --------------------------------------------------------------- | | SMTP\_HOST | `-` | SMTP server; email stays off until this and `SMTP_FROM` are set | | SMTP\_PORT | `587` | SMTP server port | | SMTP\_USERNAME | `-` | Login for the SMTP server, if it needs one | | SMTP\_PASSWORD | `-` | Password for the SMTP server | | SMTP\_FROM | `-` | Sender address, such as `romm@example.com` | | SMTP\_SECURITY | `starttls` | `starttls`, `tls` (implicit TLS, usually port 465) or `none` | ##### Audit log | variable | default | description | | --------------------------- | ------- | ------------------------------------------------- | | AUDIT\_LOG\_RETENTION\_DAYS | `90` | Days to keep audit events; `0` keeps them forever | ##### Library conversion | variable | default | description | | ------------------------------- | ------- | --------------------------------------------------------------------- | | ROM\_CONVERTO\_ENABLED | `false` | Enable the "Convert library" task and `?format=` download conversions | | ROM\_CONVERTO\_TIMEOUT | `600` | Seconds per rom-converto operation | | ROM\_CONVERTO\_MAX\_CONCURRENCY | `2` | Concurrent conversions per web or task worker | ##### Devices and sync | variable | default | description | | ------------------------------------------ | ------------------------------ | --------------------------------------------------------------------------------------------------------- | | DEVICE\_INSTALL\_ENABLED | `true` | Let users push a ROM to one of their devices for install | | DEVICE\_INSTALL\_REQUEST\_TTL\_DAYS | `2` | Days an unfinished install request waits for its device; `0` waits forever | | DEVICE\_INSTALL\_EXCLUDED\_PLATFORM\_SLUGS | `win,win3x,win9x,windows-apps` | Platforms that can never be pushed to a device | | SYNC\_RETROARCH\_PSP\_SERIAL\_MAP | `{}` | JSON map of PSP serial to extensionless ROM file name, for RetroArch PSP saves whose title matches no ROM | ##### Scheduled cleanups | variable | default | description | | ------------------------------------------ | -------------- | -------------------------------------- | | ENABLE\_SCHEDULED\_CLEANUP\_NETPLAY | `true` | Clean up empty netplay rooms | | SCHEDULED\_CLEANUP\_NETPLAY\_CRON | `*/30 * * * *` | When that cleanup runs | | ENABLE\_SCHEDULED\_CLEANUP\_UPLOAD\_TMP | `true` | Clean up abandoned chunked uploads | | SCHEDULED\_CLEANUP\_UPLOAD\_TMP\_CRON | `0 * * * *` | When that cleanup runs | | ENABLE\_SCHEDULED\_CLEANUP\_ZIP\_CACHE | `true` | Clean up stale cached ZIP files | | SCHEDULED\_CLEANUP\_ZIP\_CACHE\_CRON | `0 4 * * *` | When that cleanup runs | | ENABLE\_SCHEDULED\_CLEANUP\_SYNC\_SESSIONS | `true` | Fail sync sessions no client completed | | SCHEDULED\_CLEANUP\_SYNC\_SESSIONS\_CRON | `23 * * * *` | When that cleanup runs | ##### General | variable | default | description | | ------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------- | | ⚠️ ROMM\_CORS\_ALLOWED\_ORIGINS | `-` | Empty now allows no cross-origin requests; a `*` allows any origin but never credentials | | ⚠️ FORWARDED\_ALLOW\_IPS | local/loopback addresses | Proxies trusted to report the client's address; was `*` | | DISABLE\_EASYRPG | `false` | Disable the EasyRPG player for everyone | | SEVEN\_ZIP\_TIMEOUT | `180` | Timeout for 7-Zip operations in seconds, was `60` | </details> <details> <summary>📡 API CHANGES</summary> | Change | Description | | --- | --- | | ⚠️ CORS denied by default | `ROMM_CORS_ALLOWED_ORIGINS` now defaults to empty instead of `*`; browser clients on another origin must be allowlisted (#&#8203;4699) | | CORS wildcard drops credentials | With `*` in `ROMM_CORS_ALLOWED_ORIGINS`, responses no longer send `Access-Control-Allow-Credentials` (#&#8203;4699) | | Invalid credentials are unauthenticated | A malformed Basic header or an invalid/expired JWT bearer no longer 500s; the request proceeds unauthenticated and gets the route's 401/403 (#&#8203;4799) | | ⚠️ `PermissionGroupSchema.is_system` removed | Replaced by `system_key` (`SystemGroupKey` or null) (#&#8203;4767) | | Age limits on permissions | `PermissionGroupSchema/Create/Update` and `UserPermissionsSchema/Update` gain `age_limit`, `hide_unrated_roms`; updates apply them only with `set_age_settings: true` (#&#8203;5081) | | Collection `rom_count` is computed | `rom_count` on collection, smart and virtual collection schemas is now read-only, derived from the visible `rom_ids` (#&#8203;4930) | | ⚠️ Collection name errors | Duplicate name on create or rename returns 409 (was 500); names over 400 chars return 422 (#&#8203;4997) | | ⚠️ `GET /api/netplay/list` | Scope `assets.read` - `roms.read`; `game_id` is now an integer ROM id, 404 for a missing or hidden ROM (#&#8203;4700) | | ⚠️ Netplay socket rooms | `open-room` and password-less `join-room` require a signed-in user with `roms.read` who can see the ROM; the room password moved from `extra.room_password` to top-level `password` (#&#8203;4700) | | ⚠️ `GET /api/play-sessions` | Without `devices.read`, only allowed when `device_id` equals the calling token's device; no longer defaults `device_id` from the token (#&#8203;4956) | | ⚠️ `PlaySessionSchema.sync_session_id` removed | Field dropped from play session responses (#&#8203;4670) | | Page params on play and sync sessions | `GET /api/play-sessions` and `GET /api/sync/sessions` use shared page params (`limit` 1 to 10000, `offset`); sync sessions gains `offset` (#&#8203;4715) | | ⚠️ Sync `delete` operation | `SyncOperationSchema.action` adds `delete` (slot emptied on the server); `SyncNegotiateResponse` adds `total_delete` (#&#8203;4674) | | `POST /api/sync/negotiate` payload | New `restore_unlisted` and `emulators` fields to re-offer unlisted saves and filter by emulator (#&#8203;5091) | | Save sync baselines | `POST /api/saves` and `PUT /api/saves/{id}` take a `content_hash` query param; `POST /api/saves/{id}/downloaded` takes `content_hash` in the body (#&#8203;4768) | | Save/state uploads validated | `emulator` must be a single folder name (max length enforced); file names over 255 bytes return 400; uploads to a hidden ROM return 404 (#&#8203;4851, #&#8203;4997) | | `SaveSchema` / `StateSchema` annotations | New `is_favorite` and `labels`; masked to defaults on another user's shared save or state (#&#8203;4714) | | `StateSchema.core` | New field naming the RetroArch core that wrote the state (#&#8203;4862) | | ⚠️ `PUT /api/roms/{id}` rename errors | Renaming onto an existing file returns 409 (was 500); an invalid or overlong `fs_name` returns 400 before any metadata fetch (#&#8203;4997) | | `PUT /api/roms/{id}` manual metadata | `raw_manual_metadata` is validated, 422 when malformed; `ManualMetadata` gains `alternative_names` (#&#8203;5111) | | `POST /api/roms/{id}/convert-to-folder` | Also returns 409 when an `.m3u` beside the file lists it; upload and walkthrough routes reject the same case (#&#8203;5076) | | `GET /api/roms` relevance ordering | Empty `order_by` with a search term ranks by relevance on every database; `char_index` is empty under relevance order (#&#8203;4996) | | `GET/HEAD /api/roms/{id}/content/{file_name}` | New `?format=` for converted single-file downloads (202 + `Retry-After` while converting, 406 when unavailable) (#&#8203;4999) | | `GET /api/roms/{id}/content/{file_name}` | New `purpose=download\|play` query param, recorded in the audit log (#&#8203;4748) | | `DetailedRomSchema.download_formats` | Formats the caller can request with `?format=` (#&#8203;5000) | | `is_easyrpg_game` on ROM schemas | New boolean on `SimpleRomSchema` and `DetailedRomSchema` (#&#8203;5077) | | Pinned media | `RomUserSchema.pinned_media` and `RomUserData.pinned_media` (ordered media keys, null for default) (#&#8203;4764) | | ROM metadata fields | LaunchBox box art `box2d_*`/`box3d_*` (url and path), `video_path` now nullable (#&#8203;5129); SS `physical_disc`/`physical_extra_discs` (#&#8203;4885); SS and Hasheous `dump_regions/languages/tags` (#&#8203;4740); RA `hash_match` (#&#8203;4816) | | `PlatformSchema` | New computed `abbreviation` and `alternative_names` (#&#8203;5107) | | `MusicTrackSchema.file_name` | New field (#&#8203;5079) | | `DeviceSchema.capabilities` | New `capabilities` map on device schema and on `POST`/`PUT /api/devices` payloads (#&#8203;4835) | | `GET /api/heartbeat` | Adds `EMULATION.DISABLE_EASYRPG` (#&#8203;5077), `NOTIFICATIONS` (#&#8203;4735), `DEVICE_INSTALL` (#&#8203;4835), `CONVERTO` (#&#8203;4999) | | `GET /api/config` | Adds `CONVERTO` and `CONVERTO_LIBRARY_TARGETS` (#&#8203;4999) | | `POST /api/tasks/run/{task_name}` | Returns 409 when a single-instance task is already queued or running; `TaskInfo` adds `destructive` (#&#8203;5000) | | `POST /api/reset-password` | Enforces the password policy with 400, leaving the reset link usable (#&#8203;5106) | | OIDC callback | A provider error redirects to `/login?bypass_autologin=true` instead of 500 (#&#8203;4746); an account linked to a different provider identity gets 403 (#&#8203;4892) | | `POST /api/activity/heartbeat` | Returns 404 for a ROM hidden from the caller (#&#8203;5117) | | ⚠️ `activity:update` socket event | Sent only to users who can see the ROM, instead of broadcast to every client (#&#8203;5121) | | ⚠️ `scan` socket event payload | Validated strictly (`ScanPayload`, unknown keys rejected); invalid options, no scan worker, or a scan in flight emit `scan:done_ko` (#&#8203;5014) | | `scan:scanning_rom` | Payload adds `is_new` (#&#8203;4867) | | `sync:conflict` | Payload adds `rom_name` (#&#8203;4552) | | Socket session revocation | Logging out or revoking a session disconnects the sockets it opened (#&#8203;4732) | | Streaming session routes | Control routes take `container` and `claimed_at` query params; launch socket payloads add `claimed_at`, `core`, `core_tier`, import `refusals`; `StreamingContainerSchema` adds `supports_live_states`, `import_kinds`, `state_core`; 409 bodies use `ContainerBusyDetail` (#&#8203;4631, #&#8203;4691, #&#8203;4796, #&#8203;4859, #&#8203;4862) | ##### Notifications | Method | Path | Description | | ------ | -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | GET | `/api/notifications` | List the caller's notifications, newest first ([#&#8203;4732](https://github.com/rommapp/romm/issues/4732)) | | POST | `/api/notifications` | Send a notification to yourself, or to other users with `users.write` ([#&#8203;4732](https://github.com/rommapp/romm/issues/4732)) | | POST | `/api/notifications/read` | Mark the given notifications (or all) read ([#&#8203;4732](https://github.com/rommapp/romm/issues/4732)) | | DELETE | `/api/notifications/{notification_id}` | Dismiss one notification ([#&#8203;4732](https://github.com/rommapp/romm/issues/4732)) | | DELETE | `/api/notifications` | Dismiss all notifications ([#&#8203;4732](https://github.com/rommapp/romm/issues/4732)) | ##### Notification channels | Method | Path | Description | | ------ | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | GET | `/api/notification-channels` | List the caller's channels (Apprise, webhook, email) ([#&#8203;4735](https://github.com/rommapp/romm/issues/4735)) | | POST | `/api/notification-channels` | Create a channel; email addresses get a confirmation code ([#&#8203;4735](https://github.com/rommapp/romm/issues/4735)) | | PATCH | `/api/notification-channels/{channel_id}` | Update a channel ([#&#8203;4735](https://github.com/rommapp/romm/issues/4735)) | | DELETE | `/api/notification-channels/{channel_id}` | Delete a channel ([#&#8203;4735](https://github.com/rommapp/romm/issues/4735)) | | POST | `/api/notification-channels/{channel_id}/test` | Send a test notification now ([#&#8203;4735](https://github.com/rommapp/romm/issues/4735)) | | POST | `/api/notification-channels/{channel_id}/confirm` | Confirm an email channel with its code ([#&#8203;4735](https://github.com/rommapp/romm/issues/4735)) | | POST | `/api/notification-channels/{channel_id}/resend-code` | Resend the email confirmation code ([#&#8203;4735](https://github.com/rommapp/romm/issues/4735)) | | GET | `/api/notification-channels/apprise-services` | List Apprise services and their fields (admin) ([#&#8203;4769](https://github.com/rommapp/romm/issues/4769)) | | POST | `/api/notification-channels/apprise-services/parse` | Parse a service or Apprise URL into fields (admin) ([#&#8203;4769](https://github.com/rommapp/romm/issues/4769)) | ##### Audit events | Method | Path | Description | | ------ | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | GET | `/api/audit-events` | Paginated audit log, filterable by actor, action, category, target, time range and search; admins with `users.read` see everyone ([#&#8203;4748](https://github.com/rommapp/romm/issues/4748)) | ##### Device installs | Method | Path | Description | | ------ | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------- | | GET | `/api/devices/online` | IDs of the caller's devices with an open `/devices` socket ([#&#8203;4835](https://github.com/rommapp/romm/issues/4835)) | | POST | `/api/devices/{device_id}/installs` | Queue a ROM install on a device ([#&#8203;4835](https://github.com/rommapp/romm/issues/4835)) | | GET | `/api/devices/{device_id}/installs` | List the device's pending and taken install requests ([#&#8203;4835](https://github.com/rommapp/romm/issues/4835)) | | POST | `/api/devices/{device_id}/installs/claim` | Device claims its pending requests (device-bound token only) ([#&#8203;4835](https://github.com/rommapp/romm/issues/4835)) | | PUT | `/api/devices/{device_id}/installs/{request_id}` | Device reports `done`, `already_installed` or `failed` ([#&#8203;4835](https://github.com/rommapp/romm/issues/4835)) | | DELETE | `/api/devices/{device_id}/installs/{request_id}` | Cancel a pending or taken request ([#&#8203;4835](https://github.com/rommapp/romm/issues/4835)) | | GET | `/api/roms/{id}/installs` | The caller's open install requests for a ROM ([#&#8203;4835](https://github.com/rommapp/romm/issues/4835)) | ##### RetroArch Cloud Sync (WebDAV) | Method | Path | Description | | ------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | | OPTIONS | `/api/sync/retroarch/{path}` | Advertise DAV support ([#&#8203;3904](https://github.com/rommapp/romm/issues/3904)) | | PROPFIND | `/api/sync/retroarch/{path}` | Read-only browsing of `roms/`, `saves/` and `states/` ([#&#8203;3904](https://github.com/rommapp/romm/issues/3904)) | | GET, HEAD | `/api/sync/retroarch/{path}` | Serve the manifest or a save/state file ([#&#8203;3904](https://github.com/rommapp/romm/issues/3904)) | | PUT | `/api/sync/retroarch/{path}` | Upload a save/state, matched to a ROM by file name ([#&#8203;3904](https://github.com/rommapp/romm/issues/3904)) | | DELETE, MOVE | `/api/sync/retroarch/{path}` | Remove a save/state the client dropped ([#&#8203;3904](https://github.com/rommapp/romm/issues/3904)) | | MKCOL, LOCK, UNLOCK | `/api/sync/retroarch/{path}` | Accepted no-ops for WebDAV clients; uses HTTP Basic with a 401 challenge ([#&#8203;3904](https://github.com/rommapp/romm/issues/3904)) | ##### Saves and states | Method | Path | Description | | ------ | ---------------------------- | --------------------------------------------------------------------------------------------- | | PUT | `/api/saves/{id}/favorite` | Set `is_favorite` on a save ([#&#8203;4714](https://github.com/rommapp/romm/issues/4714)) | | PUT | `/api/saves/{id}/labels` | Replace a save's labels ([#&#8203;4714](https://github.com/rommapp/romm/issues/4714)) | | PUT | `/api/saves/{id}/file-name` | Rename a save file and its screenshot ([#&#8203;4749](https://github.com/rommapp/romm/issues/4749)) | | PUT | `/api/states/{id}/favorite` | Set `is_favorite` on a state ([#&#8203;4714](https://github.com/rommapp/romm/issues/4714)) | | PUT | `/api/states/{id}/labels` | Replace a state's labels ([#&#8203;4714](https://github.com/rommapp/romm/issues/4714)) | | PUT | `/api/states/{id}/file-name` | Rename a state file and its screenshot ([#&#8203;4749](https://github.com/rommapp/romm/issues/4749)) | ##### Collections | Method | Path | Description | | ------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------- | | PUT | `/api/collections/{id}/visibility` | Share or unshare a collection (owner only) ([#&#8203;4749](https://github.com/rommapp/romm/issues/4749)) | | PUT | `/api/collections/smart/{id}/visibility` | Share or unshare a smart collection (owner only) ([#&#8203;4749](https://github.com/rommapp/romm/issues/4749)) | ##### ROMs, tasks and config | Method | Path | Description | | ------ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | POST | `/api/tasks/scan` | Queue a library scan with a `ScanPayload` body; 202, 409 if one is in flight, 503 with no worker ([#&#8203;5014](https://github.com/rommapp/romm/issues/5014)) | | GET | `/api/roms/{id}/easyrpg/{path}` | Serve the EasyRPG `index.json` and game/RTP files for RPG Maker 2000/2003 games ([#&#8203;5077](https://github.com/rommapp/romm/issues/5077)) | | PUT | `/api/config/converto_settings` | Update download conversion settings ([#&#8203;4999](https://github.com/rommapp/romm/issues/4999)) | ##### Socket.IO events | Method | Path | Description | | ---------------- | ---------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | | Server to client | `notifications:new` / `notifications:read` / `notifications:dismissed` | Notification created, marked read, or dismissed for the user ([#&#8203;4732](https://github.com/rommapp/romm/issues/4732)) | | Server to client | `install:updated` | An install request changed, sent to the owner's tabs ([#&#8203;4835](https://github.com/rommapp/romm/issues/4835)) | | Server to client | `install:queued` / `install:cancelled` | Sent to a device on the new `/devices` namespace (device-bound client token required) ([#&#8203;4835](https://github.com/rommapp/romm/issues/4835)) | | Server to client | `activity:refresh` | Signals clients to refetch the activity list ([#&#8203;5125](https://github.com/rommapp/romm/issues/5125)) | </details> #### Highlights ##### Notifications RomM now keeps a [notification inbox](https://docs.romm.app/5.4.0-alpha/using/notifications/) for each user. Scans and tasks that finish or fail, a role change, or someone ending your stream all leave a notification that is pushed to every open tab and still waits for you the next time you open RomM. Admins can send their own from the Send tab on the Notifications page, and any API client can post one through `POST /api/notifications`. [#&#8203;4732](https://github.com/rommapp/romm/issues/4732) Each user can also forward their notifications to [channels](https://docs.romm.app/5.4.0-alpha/using/notifications/#channels), filtered by level and topic. A RomM webhook sends a JSON payload ([signed with HMAC-SHA256](https://docs.romm.app/5.4.0-alpha/using/notifications/#verifying-the-signature) when you give it a secret), and email works once the [`SMTP_*` variables](https://docs.romm.app/5.4.0-alpha/administration/email/) are set, which also sends password reset links by email. Admins can additionally pick any of the services [Apprise](https://github.com/caronc/apprise) supports, such as Discord, Telegram, Slack, ntfy, Gotify or Matrix, and fill in that service's fields or paste its URL. [#&#8203;4735](https://github.com/rommapp/romm/issues/4735) [#&#8203;4769](https://github.com/rommapp/romm/issues/4769) <img width="1250" height="687" alt="Screenshot 2026-10-06 at 6 52 34 AM" src="https://github.com/user-attachments/assets/733f2235-fc07-471b-b811-7c6eafa22333" /> ##### RetroArch Cloud Sync RomM is now a Cloud Sync target for RetroArch. Point RetroArch's Cloud Sync at `https://<your-romm>/api/sync/retroarch/` (the trailing slash matters) and sign in with your RomM username and password ([setup guide](https://docs.romm.app/5.4.0-alpha/ecosystem/retroarch-cloud-sync/)). Saves and states sync straight into your library, web player states show up in RetroArch's numbered load slots, and RetroArch registers itself as one of your devices on its first sync. [#&#8203;3904](https://github.com/rommapp/romm/issues/3904) The config, thumbnails and system file categories sync too, stored per user. PSP save folders are bundled into one save per folder; if a folder's title matches no ROM, map its serial with [`SYNC_RETROARCH_PSP_SERIAL_MAP`](https://docs.romm.app/5.4.0-alpha/ecosystem/retroarch-cloud-sync/#psp-saves). Generic WebDAV clients can [browse the same path](https://docs.romm.app/5.4.0-alpha/ecosystem/retroarch-cloud-sync/#browsing-with-other-webdav-clients) read-only. [#&#8203;3931](https://github.com/rommapp/romm/issues/3931) ##### Browser saves sync, and installs on your devices All five in-browser players (EmulatorJS, EasyRPG, js-dos, PICO-8 and Ruffle) now [sync saves through device sync](https://docs.romm.app/5.4.0-alpha/using/saves-and-states/#syncing-saves-between-browsers-and-devices). Each browser profile registers as a device, named like "Firefox on macOS", and negotiates its saves with the server when a game launches, so a save made in one browser is waiting in another and a conflicting copy is archived instead of overwritten. A new [Devices page](https://docs.romm.app/5.4.0-alpha/using/devices/) at `/devices` lets you rename your devices, turn sync off for one, or remove it. [#&#8203;5091](https://github.com/rommapp/romm/issues/5091) You can also send a game to a device from the web UI. ["Install on device"](https://docs.romm.app/5.4.0-alpha/using/devices/#install-on-device) in a game's menu adds it to the download queue of the devices you pick, and the device downloads it the next time it's online. Only devices whose app reports it accepts installs are listed. Turn the feature off with `DEVICE_INSTALL_ENABLED=false`, and keep platforms out of it with `DEVICE_INSTALL_EXCLUDED_PLATFORM_SLUGS`. [#&#8203;4835](https://github.com/rommapp/romm/issues/4835) <img width="801" height="420" alt="Screenshot 2026-10-06 at 6 53 03 AM" src="https://github.com/user-attachments/assets/b314fb20-9a07-471a-a014-3c62abf665b6" /> ##### Parental controls Give a permission group an age limit, and its members only see games rated for that age or younger. Each user can replace their group's limit, and a "Hide unrated games" switch hides games no rating covers. Admins are never limited. Set them in the group and user dialogs under Administration. [#&#8203;5081](https://github.com/rommapp/romm/issues/5081) [#&#8203;5083](https://github.com/rommapp/romm/issues/5083) A game's age comes from the [strictest](https://docs.romm.app/5.4.0-alpha/administration/parental-controls/#how-a-games-age-is-worked-out) of its IGDB, ScreenScraper, LaunchBox (ESRB) and Steam ratings, and a manual rating list replaces the providers' ratings. The limit applies everywhere a game can show up: the gallery, search, collections, stats, the Jukebox and feeds. See [Parental controls](https://docs.romm.app/5.4.0-alpha/administration/parental-controls/) for the details. <img width="694" height="160" alt="Screenshot 2026-10-06 at 6 53 46 AM" src="https://github.com/user-attachments/assets/5b72b956-4d70-42c2-9f36-ccbff590c723" /> ##### Audit log RomM now records who did what: downloads and player launches, play sessions, uploads and edits, collection changes, scans and tasks, and security events like sign-ins, failed sign-ins and permission changes. Admins read it in the new Events tab under Settings › Logs, filtered by user, category and date, with a search over names and IP addresses. Events are kept for 90 days, set by [`AUDIT_LOG_RETENTION_DAYS`](https://docs.romm.app/5.4.0-alpha/administration/audit-log/#retention) (`0` keeps them forever). See [Audit log](https://docs.romm.app/5.4.0-alpha/administration/audit-log/) for everything that's recorded. [#&#8203;4748](https://github.com/rommapp/romm/issues/4748) <img width="754" height="550" alt="Screenshot 2026-10-06 at 6 54 34 AM" src="https://github.com/user-attachments/assets/fbc8fa95-10c5-4f16-990b-2caad5f64a9e" /> ##### Library conversion with rom-converto Pick a storage format per platform, and the new ["Convert library" task](https://docs.romm.app/5.4.0-alpha/administration/library-conversion/#the-convert-library-task) converts every matched game on it in place, so a game keeps its saves and collections. Only lossless conversions are offered, and because the task deletes the originals it asks you to type a confirmation first. Set `ROM_CONVERTO_ENABLED=true` and choose formats under the new Conversion settings page, or in `config.yml` ([setup guide](https://docs.romm.app/5.4.0-alpha/administration/library-conversion/)). [#&#8203;4999](https://github.com/rommapp/romm/issues/4999) With `download_conversion_enabled` on, the game page's More menu offers ["Download as"](https://docs.romm.app/5.4.0-alpha/using/downloads/#downloading-in-another-format) in any format the file can be converted to, and clients can ask for one with `?format=` on the download URL. [#&#8203;5000](https://github.com/rommapp/romm/issues/5000) ```yaml converto: download_conversion_enabled: true platform_formats: psx: chd ngc: rvz ``` ##### RPG Maker 2000/2003 in the browser RPG Maker 2000 and 2003 games on the `rpg-maker` platform now play in the browser on the [EasyRPG](https://docs.romm.app/5.4.0-alpha/using/in-browser-play/easyrpg/) web player. The free EasyRPG [RTP](https://docs.romm.app/5.4.0-alpha/using/in-browser-play/easyrpg/#the-rtp) ships with it, so games that rely on the RTP start, though some assets (mostly in battles) are missing from it. Games need to be extracted folders, since the web player can't read archives. Disable it with `DISABLE_EASYRPG`. [#&#8203;5077](https://github.com/rommapp/romm/issues/5077) <img width="1083" height="807" alt="Screenshot 2026-10-06 at 6 56 37 AM" src="https://github.com/user-attachments/assets/5a1e33f3-4830-4f61-8de4-532ee140ce2c" /> ##### Alternative titles and relevance in search [Search](https://docs.romm.app/5.4.0-alpha/using/#search) now matches the alternative titles your metadata providers know about, including localized names, so a Japanese title finds the game listed under its English name and the other way around. You can add your own in the new "Alternative titles" field of the Edit dialog, such as "ACNH" or a fan translation's name. Already matched IGDB and Hasheous games pick up their regional titles on their next metadata refresh. [#&#8203;5111](https://github.com/rommapp/romm/issues/5111) The Search page also orders results by relevance until you pick a sort, with exact title matches first. [#&#8203;4995](https://github.com/rommapp/romm/issues/4995) [#&#8203;4996](https://github.com/rommapp/romm/issues/4996) #### Minor changes - feat: Offer the desktop shell's emulator from the save/state play page by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4541](https://github.com/rommapp/romm/pull/4541) - feat(metadata): take region and language from the dump a hash matched by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4664](https://github.com/rommapp/romm/pull/4664) - feat(metadata): read a fan translation's tag and target language by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4665](https://github.com/rommapp/romm/pull/4665) - feat(saves): favorite and label saves and states by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4714](https://github.com/rommapp/romm/pull/4714) - feat(v2): multi-select saves and states by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4725](https://github.com/rommapp/romm/pull/4725) - feat(metadata): tag a dump from the flags ScreenScraper raises on it by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4666](https://github.com/rommapp/romm/pull/4666) - feat(v2): wider state cards and one timestamp across save and state views by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4736](https://github.com/rommapp/romm/pull/4736) - feat(sync): surface save conflicts detected by negotiate by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4552](https://github.com/rommapp/romm/pull/4552) - feat: promote platforms that have games in PlatformSelect by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4599](https://github.com/rommapp/romm/pull/4599) - feat: rename saves and states, and one public/private look across v2 by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4749](https://github.com/rommapp/romm/pull/4749) - feat(v2): surface WebSocket transport fallback in BackendStatusBanner by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4645](https://github.com/rommapp/romm/pull/4645) - feat(streaming): reap abandoned streaming sessions every minute by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4632](https://github.com/rommapp/romm/pull/4632) - feat(sync): tell a device when the save it holds was deleted here by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4674](https://github.com/rommapp/romm/pull/4674) - feat(streaming): resume from a foreign emulator's save or state via declared import by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4691](https://github.com/rommapp/romm/pull/4691) - feat(v2): pin any media to the game overview by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4764](https://github.com/rommapp/romm/pull/4764) - feat(streaming): name containers by their label, and harden import archives by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4796](https://github.com/rommapp/romm/pull/4796) - feat(sync): record a per-device save baseline to prove what did not change by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4768](https://github.com/rommapp/romm/pull/4768) - feat(streaming): per-platform RetroArch core override by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4859](https://github.com/rommapp/romm/pull/4859) - feat(streaming): RetroArch states carry the core that wrote them by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4862](https://github.com/rommapp/romm/pull/4862) - feat(v2): add an opt-in setting to show the game logo in place of the title on the v2 game page by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4888](https://github.com/rommapp/romm/pull/4888) - feat(soundtrack): control playback through the Media Session API by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4904](https://github.com/rommapp/romm/pull/4904) - feat(v2): promote platforms with games in the scan picker by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4897](https://github.com/rommapp/romm/pull/4897) - feat(search): search the gallery by providers' alternative titles by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4941](https://github.com/rommapp/romm/pull/4941) - feat: read play sessions across devices and page sync sessions by [@&#8203;beemines](https://github.com/beemines) in [#&#8203;4956](https://github.com/rommapp/romm/pull/4956) - feat: make netplay and upload cleanup schedules configurable by [@&#8203;beemines](https://github.com/beemines) in [#&#8203;4957](https://github.com/rommapp/romm/pull/4957) - feat(search): rank multi-word searches by phrase on PostgreSQL by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4977](https://github.com/rommapp/romm/pull/4977) - feat(input): let the D-pad move through and out of text fields by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4993](https://github.com/rommapp/romm/pull/4993) - feat(search): Search provider aliases through search\_titles and drop generated\_search\_aliases by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5003](https://github.com/rommapp/romm/pull/5003) - feat(converto): read per-file title ids with rom-converto during scans by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5031](https://github.com/rommapp/romm/pull/5031) - feat(i18n): follow the browser's language by default, with an "Auto" language option by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5051](https://github.com/rommapp/romm/pull/5051) - feat(api): Add a REST endpoint to start a library scan by [@&#8203;ilyas-hallak](https://github.com/ilyas-hallak) in [#&#8203;5014](https://github.com/rommapp/romm/pull/5014) - feat(soundtrack): play SPC, VGM, VGZ and GYM soundtracks in the browser by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5079](https://github.com/rommapp/romm/pull/5079) - feat(v2): add an `active` toggle state to RBtn by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5090](https://github.com/rommapp/romm/pull/5090) - feat(v2): resume jukebox music after a reload by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5095](https://github.com/rommapp/romm/pull/5095) - feat(v2): scope stored preferences to the signed-in user by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5098](https://github.com/rommapp/romm/pull/5098) - feat(launchbox): scrape box front, back, spine and 3D box art by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5129](https://github.com/rommapp/romm/pull/5129) - feat: search platforms by slug, abbreviation and alternative names by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5107](https://github.com/rommapp/romm/pull/5107) - feat(v2): show a top-bar indicator while a "Download as" conversion runs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5157](https://github.com/rommapp/romm/pull/5157) - feat(sync): sync RetroArch Cloud Sync saves through save slots by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5170](https://github.com/rommapp/romm/pull/5170) - feat(permissions): gate emulator streaming and in-browser play behind group permissions by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5185](https://github.com/rommapp/romm/pull/5185) #### Fixes - fix(cloud-sync): remap web-player states into RetroArch's numbered load slots by [@&#8203;fmustafayaman](https://github.com/fmustafayaman) in [#&#8203;3933](https://github.com/rommapp/romm/pull/3933) - fix(roms): order a playlist by disc number, not by file name by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4737](https://github.com/rommapp/romm/pull/4737) - fix(v2): run desktop list rows to the screen edges by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4739](https://github.com/rommapp/romm/pull/4739) - fix(roms): order lettered discs like numbered ones by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4738](https://github.com/rommapp/romm/pull/4738) - fix(scan): keep a hash source's tags when a scan skips it by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4740](https://github.com/rommapp/romm/pull/4740) - fix(docker): stop the watchdog from spawning duplicate RQ workers by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4743](https://github.com/rommapp/romm/pull/4743) - fix(hltb): retry a separated title with the separator dropped by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4759](https://github.com/rommapp/romm/pull/4759) - fix(v2): handle copy to clipboard over plain HTTP in logs and files tab by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4757](https://github.com/rommapp/romm/pull/4757) - fix(hltb): match search results by their aliases too by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4760](https://github.com/rommapp/romm/pull/4760) - fix(hltb): accept token-only sessions and discover the endpoint when GitHub fails by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4758](https://github.com/rommapp/romm/pull/4758) - fix(v2): version ROM file URLs with the file's own timestamp by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4762](https://github.com/rommapp/romm/pull/4762) - fix(auth): redirect instead of 500 when the OIDC callback is rejected by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4746](https://github.com/rommapp/romm/pull/4746) - fix(streaming): bind every session action to the claim that owns it by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4631](https://github.com/rommapp/romm/pull/4631) - fix(sync): give a sync session the life of one launch, and drop the dead play-session link by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4670](https://github.com/rommapp/romm/pull/4670) - fix(steam): read appdetails envelope keyed by another app ID by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4774](https://github.com/rommapp/romm/pull/4774) - fix(scan): keep title-id settled file categories and report id-only changes on refresh by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;4720](https://github.com/rommapp/romm/pull/4720) - fix(security): deny cross-origin requests by default by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4699](https://github.com/rommapp/romm/pull/4699) - fix(backend): detect top-level rom files at any folder depth by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4776](https://github.com/rommapp/romm/pull/4776) - fix(soundtrack): fall back to the default cover when a game has no artwork by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4779](https://github.com/rommapp/romm/pull/4779) - fix(security): authorize netplay rooms on ROM access by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4700](https://github.com/rommapp/romm/pull/4700) - fix(backend): replace the legacy Query API with Select and type the session decorators by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4730](https://github.com/rommapp/romm/pull/4730) - fix(frontend): only request platform icons that ship by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4792](https://github.com/rommapp/romm/pull/4792) - fix(player): make all Amiga Kickstarts available to PUAE by [@&#8203;yiannias](https://github.com/yiannias) in [#&#8203;4741](https://github.com/rommapp/romm/pull/4741) - fix(auth): treat invalid bearer and basic credentials as unauthenticated by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4799](https://github.com/rommapp/romm/pull/4799) - fix(auth): rename seeded permission groups to Viewer and Editor by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4767](https://github.com/rommapp/romm/pull/4767) - fix(sync): make deleted-save tracking exact, complete, and effective on filled slots by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4789](https://github.com/rommapp/romm/pull/4789) - fix(sync): follow-ups to deleted-save tracking by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4807](https://github.com/rommapp/romm/pull/4807) - fix(scan): compute RA hash for archives whose member read fails by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4806](https://github.com/rommapp/romm/pull/4806) - fix(player): assign a gamepad already connected when EmulatorJS boots by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4808](https://github.com/rommapp/romm/pull/4808) - fix(v2): drop the CRT overlay's backdrop grade on phones by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4809](https://github.com/rommapp/romm/pull/4809) - fix(playmatch): skip unhashed lookups of folder ROM members by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4812](https://github.com/rommapp/romm/pull/4812) - fix(v2): skip the post-upload scan while a scan is already running by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4810](https://github.com/rommapp/romm/pull/4810) - fix(v2): keep the focused platform tile in view when moving up the grid by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4811](https://github.com/rommapp/romm/pull/4811) - fix(ra): count RomM's own RA hash match as RetroAchievements verification by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4816](https://github.com/rommapp/romm/pull/4816) - fix(saves): stamp save and state file names with local time by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4815](https://github.com/rommapp/romm/pull/4815) - fix(ra): keep a hash match when RA's details request fails by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4822](https://github.com/rommapp/romm/pull/4822) - fix(ra): accept null Developer, Publisher and Genre from RetroAchievements by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4840](https://github.com/rommapp/romm/pull/4840) - fix(hasheous): match multi-file roms when one file is unknown by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4846](https://github.com/rommapp/romm/pull/4846) - fix(backend): restart the log forwarder and harden three Redis call sites by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4847](https://github.com/rommapp/romm/pull/4847) - fix(v2): stop URL query writes from dropping history entries; bump vue-router to 5.3.1 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4861](https://github.com/rommapp/romm/pull/4861) - fix(player): boot cue/gdi/ccd/mds disc sets whole by default by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4848](https://github.com/rommapp/romm/pull/4848) - fix(ss): map Win9x to ScreenScraper's generic Windows system by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4849](https://github.com/rommapp/romm/pull/4849) - fix(assets): reject emulator names that are not a single folder on upload by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4851](https://github.com/rommapp/romm/pull/4851) - fix(v2): keep the checkbox's native input inside its label by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4856](https://github.com/rommapp/romm/pull/4856) - fix(scan): raise the 7-Zip timeout default and keep partial bytes out of the fallback hash by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4852](https://github.com/rommapp/romm/pull/4852) - fix(v2): mount one PDF viewer at a time in the Media tab by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4853](https://github.com/rommapp/romm/pull/4853) - fix(scan): anchor rom file category to the folder below the rom root by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4854](https://github.com/rommapp/romm/pull/4854) - fix(v2): move focus by arrow key and D-pad between page regions by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4855](https://github.com/rommapp/romm/pull/4855) - fix(v2): show TheGamesDB logo on server stats coverage chips by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4866](https://github.com/rommapp/romm/pull/4866) - fix(scan): kill a stalled 7-Zip extractor at the timeout by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4868](https://github.com/rommapp/romm/pull/4868) - fix(scan): stop rescans inflating live platform game counts by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4867](https://github.com/rommapp/romm/pull/4867) - fix(platforms): stop labeling C128 as Commodore 64 in the platform picker by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4874](https://github.com/rommapp/romm/pull/4874) - fix(player): keep disabled EmulatorJS cheats out of the core by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4876](https://github.com/rommapp/romm/pull/4876) - fix(player): restart the core after loading a save it did not boot with by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4877](https://github.com/rommapp/romm/pull/4877) - fix(roms): pick grouped representatives among siblings the user can see by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4878](https://github.com/rommapp/romm/pull/4878) - fix(player): keep arcade BIOS archives whole on EmulatorJS 4.2.3 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4879](https://github.com/rommapp/romm/pull/4879) - fix(tests): redact provider credentials when recording VCR cassettes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4886](https://github.com/rommapp/romm/pull/4886) - fix(netplay): connect over websocket only by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4889](https://github.com/rommapp/romm/pull/4889) - fix(ss): keep ScreenScraper disc art for every disc of a multi-disc game by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4885](https://github.com/rommapp/romm/pull/4885) - fix(auth): trust OIDC\_TLS\_CACERTFILE alongside the default CAs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4891](https://github.com/rommapp/romm/pull/4891) - fix(oidc): fetch claims missing from the ID token from the UserInfo endpoint by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4893](https://github.com/rommapp/romm/pull/4893) - fix(auth): match OIDC users by their sub claim so an email change still logs in by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4892](https://github.com/rommapp/romm/pull/4892) - fix(v2): pin trailing: false on native reprobe throttle by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4928](https://github.com/rommapp/romm/pull/4928) - fix(frontend-v2): show the gallery scrollbar and add a back-to-top button by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4894](https://github.com/rommapp/romm/pull/4894) - fix(v2): match RSelect stacked label spacing to text fields by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4942](https://github.com/rommapp/romm/pull/4942) - fix(scan): reuse unchanged empty files on quick scan by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4949](https://github.com/rommapp/romm/pull/4949) - fix: serialize single-file ROM promotion across gunicorn workers by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4898](https://github.com/rommapp/romm/pull/4898) - fix(streaming): let the Webstation frame use camera, microphone and clipboard by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4923](https://github.com/rommapp/romm/pull/4923) - fix(auth): hash passwords with bcrypt directly and upgrade to bcrypt 5 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4916](https://github.com/rommapp/romm/pull/4916) - fix(roms): list the plain filename first in Content-Disposition by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4939](https://github.com/rommapp/romm/pull/4939) - fix(backend): keep derived rom state in step with its source by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4931](https://github.com/rommapp/romm/pull/4931) - fix(ra): match RetroAchievements by RA hash only, never by Hasheous' game ID by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4962](https://github.com/rommapp/romm/pull/4962) - fix(v2): hide the game Achievements tab when the ROM has no achievements by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4968](https://github.com/rommapp/romm/pull/4968) - fix(player): EmulatorJS multi-disk boot, Safari fullscreen quit, sparse gamepads, netplay audio, mame2003\_plus default by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4953](https://github.com/rommapp/romm/pull/4953) - fix(a11y): dark mode typography is WCAG AA safe by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4955](https://github.com/rommapp/romm/pull/4955) - fix(tools): make generate\_test\_data work on PostgreSQL by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4978](https://github.com/rommapp/romm/pull/4978) - fix(tests): give each event loop its own fake async Redis client by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4979](https://github.com/rommapp/romm/pull/4979) - fix(streaming): let the desktop session own the controller by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4974](https://github.com/rommapp/romm/pull/4974) - fix(v2): render the v2 pair page and share the auth footer by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4980](https://github.com/rommapp/romm/pull/4980) - fix(firmware): store uploads under the name written to disk by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4986](https://github.com/rommapp/romm/pull/4986) - fix(v2): check the active sort in the gallery sort menu by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4998](https://github.com/rommapp/romm/pull/4998) - fix: eight bugs from hand-maintained lists drifting apart by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4991](https://github.com/rommapp/romm/pull/4991) - fix(v2): size gallery bulk actions for whole-result selections by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4896](https://github.com/rommapp/romm/pull/4896) - fix(api): return 4xx for overlong names and duplicate collections by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4997](https://github.com/rommapp/romm/pull/4997) - fix(converto): refuse cue sheets that reach outside their folder by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5008](https://github.com/rommapp/romm/pull/5008) - fix(player): fix EmulatorJS keyboard-lock listener leak; use VueUse for v2 listener pairs by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5012](https://github.com/rommapp/romm/pull/5012) - fix(v2): hide match rename toggle when the file name would not change by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5016](https://github.com/rommapp/romm/pull/5016) - fix(backend): disable redis-py maintenance notifications probe by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5015](https://github.com/rommapp/romm/pull/5015) - fix(player): let the browser Back button leave a running player by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5020](https://github.com/rommapp/romm/pull/5020) - fix(v2): fix useResponsiveColumns observer leak; use VueUse for v2 resize observers by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5019](https://github.com/rommapp/romm/pull/5019) - fix(v2): say what a native launch reports when its session expired or a save was deleted by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5021](https://github.com/rommapp/romm/pull/5021) - fix(v2): enlarge region and language flags on v2 game cards by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5022](https://github.com/rommapp/romm/pull/5022) - fix(docker): print the configured ROMM\_PORT in the startup log by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5023](https://github.com/rommapp/romm/pull/5023) - fix(streaming): release a failed claim from one place by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5024](https://github.com/rommapp/romm/pull/5024) - fix(v2): copy over plain HTTP and skip task polling in hidden tabs by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5040](https://github.com/rommapp/romm/pull/5040) - fix(v2): size the re-sort skeleton to the known result count by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5042](https://github.com/rommapp/romm/pull/5042) - fix(scan): refresh screenshots when only their order changes by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5029](https://github.com/rommapp/romm/pull/5029) - fix(scan): keep an uploaded manual when the rom had no scraped one by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5030](https://github.com/rommapp/romm/pull/5030) - fix(input): show the focus ring when arrow keys move focus in Firefox by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;5066](https://github.com/rommapp/romm/pull/5066) - fix(db): keep iterated query results away from the cyclic GC by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5068](https://github.com/rommapp/romm/pull/5068) - fix(v2): draw under the iOS status bar and respect safe-area insets by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5075](https://github.com/rommapp/romm/pull/5075) - fix(rom): keep a disc an .m3u lists in place when promoting to a folder by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5076](https://github.com/rommapp/romm/pull/5076) - fix(v2): unsubscribe from the emitter on unmount via useEmitterEvent by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5085](https://github.com/rommapp/romm/pull/5085) - fix(v2): input follow-ups: phantom-pad right stick, LB/RB on gallery routes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5086](https://github.com/rommapp/romm/pull/5086) - fix(v2): validation, spinner, nested button and label bugs by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5088](https://github.com/rommapp/romm/pull/5088) - fix(i18n): correct es\_ES "Platform updated" string by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5089](https://github.com/rommapp/romm/pull/5089) - fix(v2): draw region flags on Chromium/Windows and center them in card chips by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5101](https://github.com/rommapp/romm/pull/5101) - fix(auth): validate reset passwords and make the last-admin guard atomic, with tests by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5106](https://github.com/rommapp/romm/pull/5106) - fix(v2): vertically align icons in Playable and LIVE chips by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5109](https://github.com/rommapp/romm/pull/5109) - fix(v2): don't re-roll header stats when switching platform tabs by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5110](https://github.com/rommapp/romm/pull/5110) - fix(sync): use only the device's own SSH key; test push-pull sync and the folder watcher by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5108](https://github.com/rommapp/romm/pull/5108) - fix(metadata): one UTC helper for release dates, a lint rule against local-time parsing, and Flashpoint tests by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5114](https://github.com/rommapp/romm/pull/5114) - fix(sync, activity): folder scan crash and hidden-ROM heartbeat; test both by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5117](https://github.com/rommapp/romm/pull/5117) - fix(patcher): read the patcher's report from its last JSON line; test the patch endpoint by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5120](https://github.com/rommapp/romm/pull/5120) - fix(activity): send play activity only to users who can see the ROM by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5121](https://github.com/rommapp/romm/pull/5121) - fix(metadata): five provider fixes; test RetroAchievements, SteamGridDB, MobyGames, Playmatch and Pouët by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5123](https://github.com/rommapp/romm/pull/5123) - fix(feeds): keep Switch files with uppercase extensions in the Tinfoil feed by [@&#8203;NotAFlightRisk](https://github.com/NotAFlightRisk) in [#&#8203;5115](https://github.com/rommapp/romm/pull/5115) - fix(activity): coalesce activity refreshes and keep one list request in flight by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5125](https://github.com/rommapp/romm/pull/5125) - fix(metadata): five provider fixes; test UPC, LaunchBox media, Demozoo, ScreenScraper and HLTB by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5126](https://github.com/rommapp/romm/pull/5126) - fix(metadata): four provider fixes; test Hasheous, TGDB, CSDb, gamelist and libretro by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5128](https://github.com/rommapp/romm/pull/5128) - fix(igdb): harden Twitch auth; test the IGDB client and handler by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5133](https://github.com/rommapp/romm/pull/5133) - fix(sync): store RetroArch Cloud Sync blobs under the assets volume by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5155](https://github.com/rommapp/romm/issues/5155) - fix(v2): name dialogs and select listboxes; axe-check story overlays by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5151](https://github.com/rommapp/romm/issues/5151) - fix(frontend): guard network-quiesced dispatch after DOM teardown by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5167](https://github.com/rommapp/romm/pull/5167) - fix(streaming): keep a slow broker from hiding save imports by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5172](https://github.com/rommapp/romm/pull/5172) - fix(sync): keep RetroArch Cloud Sync from stalling the event loop by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5171](https://github.com/rommapp/romm/pull/5171) - fix(streaming): file RetroArch streaming exits as raw saves by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5174](https://github.com/rommapp/romm/pull/5174) - fix(platforms): give platforms missing from IGDB a family by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5187](https://github.com/rommapp/romm/pull/5187) - fix(devices): count a device that polls claim as online by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5190](https://github.com/rommapp/romm/pull/5190) - fix(docker): boot when the container runs as a non-default UID by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5189](https://github.com/rommapp/romm/pull/5189) - fix(sync): resolve RetroArch paths across emulators sharing a core folder by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5191](https://github.com/rommapp/romm/pull/5191) - fix(sync): serve the newest on-disk RetroArch state in a slot by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5193](https://github.com/rommapp/romm/pull/5193) - fix(security): check collection ownership in the ROM filters by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5201](https://github.com/rommapp/romm/pull/5201) - fix(scan): reassociate a moved ROM by title id when its hashes changed by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5202](https://github.com/rommapp/romm/pull/5202) - fix(saves): make a deduplicated slot upload the slot head by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5204](https://github.com/rommapp/romm/pull/5204) - fix(oidc): match any of the comma-separated groups in OIDC\_ROLE\_\* by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5203](https://github.com/rommapp/romm/pull/5203) - fix(v2): keep a save uploaded for a core in autosave by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5205](https://github.com/rommapp/romm/pull/5205) - fix(streaming): share RetroArch stream saves and states with Cloud Sync by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5197](https://github.com/rommapp/romm/pull/5197) - fix(sync): file a screenshot sent ahead of its state past a stream capture by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5208](https://github.com/rommapp/romm/pull/5208) - fix(streaming): keep a stream capture Cloud Sync serves from its slot by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5209](https://github.com/rommapp/romm/pull/5209) - fix(config): migrate retired filesystem folder keys instead of exiting by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5211](https://github.com/rommapp/romm/pull/5211) #### Other changes - chore(deps): update mariadb docker tag to v13 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4537](https://github.com/rommapp/romm/pull/4537) - test(backend): catch column nullability drift between models and migrations by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4756](https://github.com/rommapp/romm/pull/4756) - perf(backend): read the gallery sorts out of an index by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4647](https://github.com/rommapp/romm/pull/4647) - perf(sync): batch RetroArch manifest hashing and list PROPFIND paths unhashed by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4761](https://github.com/rommapp/romm/pull/4761) - build(deps): bump uvicorn to 0.53 and uvicorn-worker to 0.4 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4729](https://github.com/rommapp/romm/pull/4729) - chore(deps): upgrade vitest and [@&#8203;vitest/ui](https://github.com/vitest/ui) to v5 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4747](https://github.com/rommapp/romm/pull/4747) - chore(deps): bump fastapi to 0.141.1 and replace fastapi-pagination with a local page model by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4715](https://github.com/rommapp/romm/pull/4715) - docs(claude): trim root CLAUDE.md and split stack guidance into nested files by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4790](https://github.com/rommapp/romm/pull/4790) - ci: run mypy in its own workflow instead of through Trunk by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4724](https://github.com/rommapp/romm/pull/4724) - chore(mypy): require coded ignores and flag stale ones, type protected\_route by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4794](https://github.com/rommapp/romm/pull/4794) - ci: nightly image, index annotations and SBOMs by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4731](https://github.com/rommapp/romm/pull/4731) - chore: delegate llm tasks to static analysis tools. by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4742](https://github.com/rommapp/romm/pull/4742) - chore(docker): verify downloads with ADD --checksum and pin RAHasher by commit by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4781](https://github.com/rommapp/romm/pull/4781) - refactor(backend): move engine-specific query SQL out of handlers into utils/sql\_dialect.py by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4793](https://github.com/rommapp/romm/pull/4793) - chore(mypy): require full annotations outside tests, type the setup responses by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4795](https://github.com/rommapp/romm/pull/4795) - fix(deps): bump mariadb connector to 1.1.14 for Python 3.14 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4814](https://github.com/rommapp/romm/pull/4814) - chore(mypy): switch to strict mode with listed exceptions by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4817](https://github.com/rommapp/romm/pull/4817) - chore(mypy): give every generic its type arguments by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4818](https://github.com/rommapp/romm/pull/4818) - test(sync): isolate RetroArch blob and PSP pending dirs per test by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4824](https://github.com/rommapp/romm/pull/4824) - refactor(backend): move FORCE INDEX hint and FULLTEXT match into sql\_dialect by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4825](https://github.com/rommapp/romm/pull/4825) - chore(mypy): drop warn\_return\_any and validate provider responses with strict JSON by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4826](https://github.com/rommapp/romm/pull/4826) - chore(backend): enforce mypy's disallow\_untyped\_calls and disallow\_untyped\_decorators by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4827](https://github.com/rommapp/romm/pull/4827) - chore(mypy): report untyped imports instead of disabling the check by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4828](https://github.com/rommapp/romm/pull/4828) - chore(deps): upgrade Authlib to 1.8 and joserfc to 1.7 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4829](https://github.com/rommapp/romm/pull/4829) - chore(deps): move from httpx to httpx2 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4830](https://github.com/rommapp/romm/pull/4830) - chore(backend): replace zstandard with stdlib compression.zstd by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4831](https://github.com/rommapp/romm/pull/4831) - chore(deps): bump uv to 0.12.19 and relax required-version for Dependabot by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4832](https://github.com/rommapp/romm/pull/4832) - fix(deps): bump asyncssh, click, pygments and requests for open advisories by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4833](https://github.com/rommapp/romm/pull/4833) - perf(gamelist): list each ES-DE media folder once per platform by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4841](https://github.com/rommapp/romm/pull/4841) - chore(deps): bump redis to 8.1 and fakeredis to 2.38, drop types-redis by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4844](https://github.com/rommapp/romm/pull/4844) - perf(scan): download a rom's media files concurrently by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4842](https://github.com/rommapp/romm/pull/4842) - chore(skills): vendor humanizer and run it after pr-ready and the release drafts by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4845](https://github.com/rommapp/romm/pull/4845) - chore(mypy): replace type-ignore defaults with typed injection sentinels by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4843](https://github.com/rommapp/romm/pull/4843) - perf(backend): pipeline multi-command Redis writes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4858](https://github.com/rommapp/romm/pull/4858) - chore(lint): ban double-cast ROM fixtures in tests by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4863](https://github.com/rommapp/romm/pull/4863) - docs: consolidate agent instructions into AGENTS.md and add a shared dev environment setup by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4869](https://github.com/rommapp/romm/pull/4869) - ci: cache mypy between runs and install only Playwright's headless shell by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4873](https://github.com/rommapp/romm/pull/4873) - chore(trunk): upgrade plugins and linters, automate upgrades, scan all files weekly by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4870](https://github.com/rommapp/romm/pull/4870) - chore(trunk): Upgrade trunk by [@&#8203;github-actions](https://github.com/github-actions)\[bot] in [#&#8203;4880](https://github.com/rommapp/romm/pull/4880) - ci: coverage upload, Docker PR check, API type drift check and other CI maintenance by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4875](https://github.com/rommapp/romm/pull/4875) - docs: fix the cloud environment setup script command in AGENTS.md by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4882](https://github.com/rommapp/romm/pull/4882) - chore(trunk): remove the no-emdash Trunk linter after a one-time sweep by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4884](https://github.com/rommapp/romm/pull/4884) - chore(storybook): Save State support by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4727](https://github.com/rommapp/romm/pull/4727) - perf: run the chroma-key placeholder check off the event loop by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4899](https://github.com/rommapp/romm/pull/4899) - ci: sign release images, gate on critical CVEs and scan for malware by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4890](https://github.com/rommapp/romm/pull/4890) - chore(deps): update dependency [@&#8203;types/node](https://github.com/types/node) to v24 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4908](https://github.com/rommapp/romm/pull/4908) - chore(deps): update dependency [@&#8203;floating-ui/vue](https://github.com/floating-ui/vue) to v2 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4907](https://github.com/rommapp/romm/pull/4907) - chore(deps): update dependency pytest-cov to v7 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4911](https://github.com/rommapp/romm/pull/4911) - chore(deps): update dependency certifi to v2026 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4915](https://github.com/rommapp/romm/pull/4915) - test(tasks): cover scheduled cron description in TasksSection by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4918](https://github.com/rommapp/romm/pull/4918) - chore(deps): update dependency cronstrue to v3 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4917](https://github.com/rommapp/romm/pull/4917) - chore(deps): update dependency ipykernel to v7 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4919](https://github.com/rommapp/romm/pull/4919) - chore(deps): update dependency globals to v17 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4913](https://github.com/rommapp/romm/pull/4913) - chore(deps): update dependency streaming-form-data to v2 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4924](https://github.com/rommapp/romm/pull/4924) - chore(deps): update dependency vite-plugin-mkcert to v2 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4925](https://github.com/rommapp/romm/pull/4925) - chore(deps): update dependency md-editor-v3 to v7 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4920](https://github.com/rommapp/romm/pull/4920) - chore(deps): update dependency [@&#8203;vueuse/core](https://github.com/vueuse/core) to v15 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4909](https://github.com/rommapp/romm/pull/4909) - refactor(frontend): derive state that was stored and synced by hand by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4929](https://github.com/rommapp/romm/pull/4929) - chore(deps): update github actions by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4910](https://github.com/rommapp/romm/pull/4910) - perf(scan): read only the selected ROMs' gamelist entries by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4940](https://github.com/rommapp/romm/pull/4940) - perf(gamelist): validate media tag paths once per directory by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4943](https://github.com/rommapp/romm/pull/4943) - test(frontend): pin the timezone for pending-asset capture names by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4948](https://github.com/rommapp/romm/pull/4948) - chore(lint): remove the no-emdash-in-comment ESLint rule after a one-time sweep by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4887](https://github.com/rommapp/romm/pull/4887) - chore(deps): update dependency pinia to v4 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4921](https://github.com/rommapp/romm/pull/4921) - chore(deps): update md-editor-v3 to v7 and fix its breaking changes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4922](https://github.com/rommapp/romm/pull/4922) - chore(deps): update dependency vuetify to v4 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4927](https://github.com/rommapp/romm/pull/4927) - refactor(db): drop stored columns that duplicate derivable data by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4930](https://github.com/rommapp/romm/pull/4930) - ci: grant issues: write to the build scan job by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4934](https://github.com/rommapp/romm/pull/4934) - chore(deps): bump markdown-it from 14.2.0 to 14.3.2 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4960](https://github.com/rommapp/romm/pull/4960) - chore(deps): bump urllib3 from 2.7.0 to 2.8.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4964](https://github.com/rommapp/romm/pull/4964) - chore(deps): update dependency axios to v1.20.0 \[security] by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4966](https://github.com/rommapp/romm/pull/4966) - chore(deps-dev): bump brace-expansion from 2.1.4 to 2.1.7 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4961](https://github.com/rommapp/romm/pull/4961) - test(backend): build test rows through shared factories and enforce them with ruff by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4972](https://github.com/rommapp/romm/pull/4972) - chore(deps): update typescript to 6.0.3 and hold below 7 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4963](https://github.com/rommapp/romm/pull/4963) - refactor: remove unused code and deduplicate copied helpers by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4975](https://github.com/rommapp/romm/pull/4975) - refactor(db): follow-ups to the derived-state fixes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4969](https://github.com/rommapp/romm/pull/4969) - refactor(db): escape LIKE searches with SQLAlchemy's autoescape by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4976](https://github.com/rommapp/romm/pull/4976) - docs: recommend pytest-xdist for local backend test runs by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4983](https://github.com/rommapp/romm/pull/4983) - perf(search): keep the FULLTEXT index on searches with short words or stopwords by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4970](https://github.com/rommapp/romm/pull/4970) - chore(deps): bump tornado from 6.5.8 to 6.5.9 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4985](https://github.com/rommapp/romm/pull/4985) - chore(wcag): AA misc fixes by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4971](https://github.com/rommapp/romm/pull/4971) - perf: trim idle memory of the backend processes by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4981](https://github.com/rommapp/romm/pull/4981) - perf: keep task code out of the cron scheduler and idle RQ workers by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4982](https://github.com/rommapp/romm/pull/4982) - chore(deps): update frontend dependencies by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4989](https://github.com/rommapp/romm/pull/4989) - chore(deps): update backend dependencies by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4990](https://github.com/rommapp/romm/pull/4990) - chore(deps): upgrade SQLAlchemy to 2.1 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5007](https://github.com/rommapp/romm/pull/5007) - refactor(db): replace deprecated noload() with raiseload() by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5009](https://github.com/rommapp/romm/pull/5009) - refactor(v2): share popover dismissal across RSelect, RComboboxField, RDateField and RMenu by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5010](https://github.com/rommapp/romm/pull/5010) - refactor(v2): route v2 server-backed search boxes through useDebouncedSearch by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5011](https://github.com/rommapp/romm/pull/5011) - refactor(v2): route remaining v2 clipboard writes through useClipboard by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5017](https://github.com/rommapp/romm/pull/5017) - test(backend): fix order-dependent test\_saves / test\_states db handler tests by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5018](https://github.com/rommapp/romm/pull/5018) - refactor(v2): track AppNav's window scroll with useWindowScroll by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5025](https://github.com/rommapp/romm/pull/5025) - test(frontend): auto-unmount every test's wrappers from the global setup by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5026](https://github.com/rommapp/romm/pull/5026) - test(frontend): give every test a fresh Pinia from the global setup by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5028](https://github.com/rommapp/romm/pull/5028) - test(frontend): share the vue-i18n test mock and let Vitest reset mocks, stubs and env by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5027](https://github.com/rommapp/romm/pull/5027) - refactor(v2): use VueUse for v2 debounces, intervals, the page title and avatar preview by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5035](https://github.com/rommapp/romm/pull/5035) - refactor(v2): use useTimeoutFn for v2's one-shot timers and useEventListener for hotkeys by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5037](https://github.com/rommapp/romm/pull/5037) - refactor(v2): add useRouteQueryParam for URL-backed v2 view state by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5039](https://github.com/rommapp/romm/pull/5039) - refactor(v2): load self-fetching v2 components through useFetchState by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5038](https://github.com/rommapp/romm/pull/5038) - test(frontend): restore real timers globally and echo params from the shared i18n mock by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5043](https://github.com/rommapp/romm/pull/5043) - refactor(v2): use VueUse for v2's rAF loops, listener pairs and grid observers by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5045](https://github.com/rommapp/romm/pull/5045) - chore(storybook): fix the RTooltip ParentAttach story so the tooltip renders by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5047](https://github.com/rommapp/romm/pull/5047) - refactor(v2): share v2's visible-tab poll and move the last timers and RTooltip to VueUse by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5048](https://github.com/rommapp/romm/pull/5048) - refactor(v2): use VueUse for v2's image previews, remaining observers, rAF loops and timers by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5052](https://github.com/rommapp/romm/pull/5052) - chore(frontend): tighten frontend typechecking by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5053](https://github.com/rommapp/romm/pull/5053) - chore(frontend): enable strictVModel and make RSelect, PlatformSelect and RTabNav generic by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5055](https://github.com/rommapp/romm/pull/5055) - chore(converto): bump rom-converto to v0.23.2 in both images by [@&#8203;DevYukine](https://github.com/DevYukine) in [#&#8203;5041](https://github.com/rommapp/romm/pull/5041) - ci: cache frontend node\_modules and skip npm ci on a lockfile match by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5044](https://github.com/rommapp/romm/pull/5044) - ci(e2e): copy frontend/assets into the preview build by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5056](https://github.com/rommapp/romm/pull/5056) - chore(frontend): split the frontend tsconfig into project references by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5057](https://github.com/rommapp/romm/pull/5057) - chore(frontend): prepare the gallery and grid-nav composables for noUncheckedIndexedAccess by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5058](https://github.com/rommapp/romm/pull/5058) - refactor(v2): render v2 QR codes with useQRCode from [@&#8203;vueuse/integrations](https://github.com/vueuse/integrations) by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5060](https://github.com/rommapp/romm/pull/5060) - chore(frontend): prepare the rest of the v2 app code for noUncheckedIndexedAccess by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5061](https://github.com/rommapp/romm/pull/5061) - chore(frontend): prepare the shared frontend code for noUncheckedIndexedAccess by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5062](https://github.com/rommapp/romm/pull/5062) - chore(frontend): enable noUncheckedIndexedAccess by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5063](https://github.com/rommapp/romm/pull/5063) - chore(frontend): prepare v2 and shared code for exactOptionalPropertyTypes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5065](https://github.com/rommapp/romm/pull/5065) - chore(frontend): enable exactOptionalPropertyTypes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5067](https://github.com/rommapp/romm/pull/5067) - perf(scan): skip the second write and reload for newly inserted roms by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5070](https://github.com/rommapp/romm/pull/5070) - perf(roms): faster smart collection and filter value queries by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5069](https://github.com/rommapp/romm/pull/5069) - refactor(backend): route ROM visibility through a single RomVisibilityFilter by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5064](https://github.com/rommapp/romm/pull/5064) - perf(config): read config.yml values with a plain dict walk by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5071](https://github.com/rommapp/romm/pull/5071) - test(db): give worktree runs their own temporary test database by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5074](https://github.com/rommapp/romm/pull/5074) - perf(tests): faster database setup and cleanup in the backend suite by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5073](https://github.com/rommapp/romm/pull/5073) - perf(frontend): smaller first load and faster vitest setup by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5072](https://github.com/rommapp/romm/pull/5072) - refactor(v2): simplify keyboard/gamepad handling by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5084](https://github.com/rommapp/romm/pull/5084) - perf(frontend): load md-editor on demand instead of with the app by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;5080](https://github.com/rommapp/romm/pull/5080) - refactor(v2): share the scan form's hash-matcher switches and scan-type select by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5099](https://github.com/rommapp/romm/pull/5099) - chore(deps): update frontend dependencies by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;5103](https://github.com/rommapp/romm/pull/5103) - chore(deps): update backend dependencies by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;5104](https://github.com/rommapp/romm/pull/5104) - chore(deps): update actions/upload-code-coverage action to v1.4.4 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;5105](https://github.com/rommapp/romm/pull/5105) - chore(deps): upgrade apprise to 2.0 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5102](https://github.com/rommapp/romm/pull/5102) - test(tasks): cover the WebP conversion task by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5112](https://github.com/rommapp/romm/pull/5112) - test(utils): read tags and covers from real audio files in each format by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5113](https://github.com/rommapp/romm/pull/5113) - test: platform fixture factories for the backend and frontend by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5119](https://github.com/rommapp/romm/pull/5119) - test: typed ROM fixtures, and no model casts in tests or stories by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5122](https://github.com/rommapp/romm/pull/5122) - refactor(metadata): share the indexed filename lookup across IGDB, Moby and SS by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5130](https://github.com/rommapp/romm/pull/5130) - refactor(metadata): share provider request retries and error mapping by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5132](https://github.com/rommapp/romm/pull/5132) - refactor(metadata): share heartbeats, filename id tags and the Moby rom builder by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5134](https://github.com/rommapp/romm/pull/5134) - chore(e2e): Update playwright with smaller focus by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4932](https://github.com/rommapp/romm/pull/4932) - chore(deps): update backend dependencies by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;5136](https://github.com/rommapp/romm/pull/5136) - chore(e2e): check every page for accessibility violations with axe by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4951](https://github.com/rommapp/romm/pull/4951) - chore(trunk): pin the CLI download to its sha256 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5140](https://github.com/rommapp/romm/pull/5140) - chore(trunk): Upgrade trunk by [@&#8203;github-actions](https://github.com/github-actions)\[bot] in [#&#8203;5137](https://github.com/rommapp/romm/pull/5137) - chore(trunk): fix the scheduled trunk check-all failures by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5141](https://github.com/rommapp/romm/pull/5141) - chore(deps): update dependency md-editor-v3 to v7.1.0 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;5142](https://github.com/rommapp/romm/pull/5142) - chore(v2): let the theme toolbar drive every lib story by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5150](https://github.com/rommapp/romm/issues/5150) - refactor(v2): extract RSortHeader and make the index lists real tables by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5149](https://github.com/rommapp/romm/issues/5149) - chore(eslint): adopt [@&#8203;vue/eslint-config-typescript](https://github.com/vue/eslint-config-typescript) and lint with type information by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5148](https://github.com/rommapp/romm/issues/5148) - chore(dev): DEV\_PROXY\_TARGET to run the frontend against a remote RomM by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4763](https://github.com/rommapp/romm/pull/4763) - chore(storybook): add an input modality toolbar by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4791](https://github.com/rommapp/romm/pull/4791) - chore(locales): it\_IT translation by [@&#8203;dms1e4](https://github.com/dms1e4) in [#&#8203;5006](https://github.com/rommapp/romm/pull/5006) - chore(v2): switch Storybook docgen to vue-component-meta and enable autodocs by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5152](https://github.com/rommapp/romm/pull/5152) - test(frontend): run Storybook stories and a11y checks in Chromium via addon-vitest by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;5153](https://github.com/rommapp/romm/pull/5153) - chore(deps): bump source-map-js from 1.2.1 to 1.2.2 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5164](https://github.com/rommapp/romm/pull/5164) - chore(deps): bump multidict from 6.7.1 to 6.9.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5165](https://github.com/rommapp/romm/pull/5165) - chore(deps): bump mako from 1.3.12 to 1.4.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5166](https://github.com/rommapp/romm/pull/5166) - chore(deps): bump postcss-selector-parser from 7.1.5 to 7.1.6 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;5163](https://github.com/rommapp/romm/pull/5163) - ci: check that PR titles follow Conventional Commits by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5143](https://github.com/rommapp/romm/pull/5143) - perf(scan): batch restored-ROM hydration reads by [@&#8203;Shreeniwas1](https://github.com/Shreeniwas1) in [#&#8203;5145](https://github.com/rommapp/romm/pull/5145) - perf(scan): replace superseded-folder prefix scans with ancestor set by [@&#8203;Shreeniwas1](https://github.com/Shreeniwas1) in [#&#8203;5146](https://github.com/rommapp/romm/pull/5146) - perf(tasks): batch RetroAchievements progress sync reads by [@&#8203;Shreeniwas1](https://github.com/Shreeniwas1) in [#&#8203;5144](https://github.com/rommapp/romm/pull/5144) - docs(readme): refresh community projects list by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5176](https://github.com/rommapp/romm/pull/5176) - ci: add network timeouts and retries to setup-backend apt calls by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5181](https://github.com/rommapp/romm/pull/5181) - ci: keep CI jobs off Ubuntu apt mirrors by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5182](https://github.com/rommapp/romm/pull/5182) - perf(tasks): batch RetroAchievements status writes, make add\_rom\_user race-safe by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5179](https://github.com/rommapp/romm/pull/5179) - perf(frontend): serve only the mdi icons the app uses by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;5118](https://github.com/rommapp/romm/pull/5118) - docs(readme): add Vita RomM to community projects by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5188](https://github.com/rommapp/romm/pull/5188) - perf(sync): resolve RetroArch core aliases in one query by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5192](https://github.com/rommapp/romm/pull/5192) - perf(frontend): serve the small cover to 1x screens by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;5184](https://github.com/rommapp/romm/pull/5184) - refactor(config): parse every comma-separated env var with one helper by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5207](https://github.com/rommapp/romm/pull/5207) - refactor(streaming): let the backend pick the resume picker's states by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;5210](https://github.com/rommapp/romm/pull/5210) #### New Contributors - [@&#8203;fmustafayaman](https://github.com/fmustafayaman) made their first contribution in [#&#8203;3931](https://github.com/rommapp/romm/pull/3931) - [@&#8203;yiannias](https://github.com/yiannias) made their first contribution in [#&#8203;4741](https://github.com/rommapp/romm/pull/4741) - [@&#8203;beemines](https://github.com/beemines) made their first contribution in [#&#8203;4956](https://github.com/rommapp/romm/pull/4956) - [@&#8203;dms1e4](https://github.com/dms1e4) made their first contribution in [#&#8203;5006](https://github.com/rommapp/romm/pull/5006) - [@&#8203;ilyas-hallak](https://github.com/ilyas-hallak) made their first contribution in [#&#8203;5014](https://github.com/rommapp/romm/pull/5014) - [@&#8203;NotAFlightRisk](https://github.com/NotAFlightRisk) made their first contribution in [#&#8203;5115](https://github.com/rommapp/romm/pull/5115) - [@&#8203;Shreeniwas1](https://github.com/Shreeniwas1) made their first contribution in [#&#8203;5145](https://github.com/rommapp/romm/pull/5145) **Full Changelog**: <https://github.com/rommapp/romm/compare/5.3.1...5.4.0> ### [`v5.3.1`](https://github.com/rommapp/romm/releases/tag/5.3.1) [Compare Source](https://github.com/rommapp/romm/compare/5.3.0...5.3.1) #### Minor changes - feat(v2): shorten the changelog's pull request links to #nnnn by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4705](https://github.com/rommapp/romm/pull/4705) #### Fixes - fix(config): show folder mappings whose folder name is not lowercase by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4676](https://github.com/rommapp/romm/pull/4676) - fix(router): re-read the rom on every entry to the details route by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4685](https://github.com/rommapp/romm/pull/4685) - fix(streaming): put every container in a pool, and reach later pools by key by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4596](https://github.com/rommapp/romm/pull/4596) - fix(security): patch OS packages in the published image and scope workflow permissions by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4592](https://github.com/rommapp/romm/pull/4592) - fix(docker): move the bases to Alpine 3.24 and nginx 1.30.5 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4602](https://github.com/rommapp/romm/pull/4602) - fix(frontend): eliminate import cycles and gate them with eslint by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4643](https://github.com/rommapp/romm/pull/4643) - fix(security): revoke the target account's sessions on a credential change by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4653](https://github.com/rommapp/romm/pull/4653) - fix(v2): declare color-scheme with the v2 theme palettes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4701](https://github.com/rommapp/romm/pull/4701) - fix(tooling): repair mypy's exclude regex and align its version with CI by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4655](https://github.com/rommapp/romm/pull/4655) - fix(frontend): keep the page title across query-only navigations by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4657](https://github.com/rommapp/romm/pull/4657) - fix(frontend): tolerate null filter\_value lists from the API by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4680](https://github.com/rommapp/romm/pull/4680) - fix(v2): restore gamepad navigation on the platforms grid and every player by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4681](https://github.com/rommapp/romm/pull/4681) - fix(v2): pad focus follow-ups to [#&#8203;4681](https://github.com/rommapp/romm/issues/4681) by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4707](https://github.com/rommapp/romm/pull/4707) - fix(backend): correct three small typing faults mypy now reports by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4709](https://github.com/rommapp/romm/pull/4709) - fix(v2): descenders in chips are displayed by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4682](https://github.com/rommapp/romm/pull/4682) - fix(v2): rebuild the gallery list view for phones by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4654](https://github.com/rommapp/romm/pull/4654) - fix(backend): make type ignores honest and replace mypy trunk-ignores with fixes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4711](https://github.com/rommapp/romm/pull/4711) - fix(backend): type fastapi-pagination pages and params for mypy by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4713](https://github.com/rommapp/romm/pull/4713) - fix(hltb): send a browser user agent so HLTB's firewall lets requests through by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4717](https://github.com/rommapp/romm/pull/4717) - fix(backend): cap music page size at the largest page the jukebox asks for by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4716](https://github.com/rommapp/romm/pull/4716) - fix(backend): type DML row counts from Session.execute for mypy by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4718](https://github.com/rommapp/romm/pull/4718) - fix(backend): build streaming response schemas directly instead of dicts by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4719](https://github.com/rommapp/romm/pull/4719) - fix(backend): clear the remaining mypy errors outside roms\_handler by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4723](https://github.com/rommapp/romm/pull/4723) - fix(security): check endpoint ownership and consume invites atomically by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4694](https://github.com/rommapp/romm/pull/4694) - fix(sigil): bump the pin so GameCube save targets name a .gci on disk by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;4687](https://github.com/rommapp/romm/pull/4687) #### Other changes - docs: add a draft-announcement skill by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4667](https://github.com/rommapp/romm/pull/4667) - chore(deps): update astral-sh/setup-uv action to v10.2.0 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4668](https://github.com/rommapp/romm/pull/4668) - chore: expand ts typecheck to `frontend/scripts/**/*.ts` by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4575](https://github.com/rommapp/romm/pull/4575) - build(frontend): prefix CSS in dev as well as production by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4609](https://github.com/rommapp/romm/pull/4609) - chore(security): harden Actions and Renovate, credit Socket in the readme by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4660](https://github.com/rommapp/romm/pull/4660) - test: fix the type errors mypy can now see in the test suite by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4708](https://github.com/rommapp/romm/pull/4708) - test(backend): simulate the CHD permission error without chmod by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4712](https://github.com/rommapp/romm/pull/4712) - ci: scan the published images, and stop scanning the lockfiles twice by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4683](https://github.com/rommapp/romm/pull/4683) - chore(v2) storybook viewport mirrors vue installBreakpointAttribute by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4710](https://github.com/rommapp/romm/pull/4710) **Full Changelog**: <https://github.com/rommapp/romm/compare/5.3.0...5.3.1> ### [`v5.3.0`](https://github.com/rommapp/romm/releases/tag/5.3.0) [Compare Source](https://github.com/rommapp/romm/compare/5.2.0...5.3.0) > \[!CAUTION] > > ## Breaking changes > > ### Filesystem structure must be explicitly declared > > We no longer detect the filesystem layout by default, and an instance will **refuses to start** until explicitly declared in your `config.yml`. This is the equivalent of **structure A**: > > ```yaml > filesystem: > structure: > default: "roms/{platform}/{game}" > firmware: "bios/{platform}" > ``` > > And the equivalent of **structure B**: > > ```yaml > filesystem: > structure: > default: "{platform}/roms/{game}" > firmware: "{platform}/bios" > ``` > > `filesystem.roms_folder` and `filesystem.firmware_folder` have also been removed. An instance that still sets either will refuse to start and print the equivalent template. > > See [Custom library structure](https://docs.romm.app/5.3.0-beta/getting-started/folder-structure/#custom-library-structure) and the [`filesystem.structure`](https://docs.romm.app/5.3.0-beta/reference/configuration-file/#filesystemstructure) schema. > \[!WARNING] > **Emulator streaming config is now one entry per container, not one per emulator.** > A container's host, protocol and label become the defaults for every platform it serves, and a platform block only overrides what differs. Existing streaming setups need their `config.yml` rewritten before the feature will come back up. > > See [Emulator Streaming](https://docs.romm.app/5.3.0-beta/using/emulator-streaming/) for the new schema. > \[!NOTE] > > ### Environment variables > > #### Emulation > > | variable | default | description | > | -------------------------------- | ------- | -------------------------------------------------------- | > | MAX\_SAVES\_PER\_SLOT | `50` | Versions kept per save slot, newest first (`0` disables) | > | STREAMING\_LAUNCH\_TIMEOUT | `600` | Seconds to wait for a streamed emulator to launch | > | STREAMING\_STATE\_HISTORY\_LIMIT | `50` | Save states kept per ROM, emulator and user | > > #### Physical games > > | variable | default | description | > | --------------------- | --------------------------------------------- | ------------------------------------------- | > | UPC\_LOOKUP\_ENABLED | `true` | Barcode lookups when adding a physical game | > | UPC\_LOOKUP\_API\_KEY | `-` | Key for the UPC lookup service | > | UPC\_LOOKUP\_URL | `https://api.upcitemdb.com/prod/trial/lookup` | UPC lookup endpoint | > > #### Recommendations > > | variable | default | description | > | ----------------------------------------- | ------------ | ------------------------------------------- | > | ENABLE\_SCHEDULED\_BUILD\_RECOMMENDATIONS | `true` | Nightly rebuild of the recommendation index | > | SCHEDULED\_BUILD\_RECOMMENDATIONS\_CRON | `30 5 * * *` | When that rebuild runs | > > #### Browser players > > | variable | default | description | > | -------------- | ------- | -------------------------------------- | > | DISABLE\_JSDOS | `false` | Disable the js-dos player for everyone | > | DISABLE\_PICO8 | `false` | Disable the PICO-8 player for everyone | > > #### Metadata providers > > | variable | default | description | > | --------------------- | ----------------------------- | ----------------------------------------------------------- | > | DEMOZOO\_API\_ENABLED | `false` | Enable the Demozoo metadata provider | > | POUET\_API\_ENABLED | `false` | Enable the Pouët metadata provider | > | CSDB\_API\_ENABLED | `false` | Enable the CSDb metadata provider | > | STEAM\_API\_ENABLED | `false` | Enable the Steam storefront metadata provider | > | HASHEOUS\_API\_URL | `https://hasheous.org/api/v1` | Use a self-hosted [Hasheous](https://hasheous.org) instance | > > #### General > > | variable | default | description | > | -------------------------- | ------- | ----------------------------------------------------- | > | SCAN\_WORKERS | `4` | Parallel scan workers, was `1` | > | WEB\_SERVER\_CONCURRENCY | `4` | API server workers, was `1` | > | DB\_POOL\_RECYCLE\_SECONDS | `300` | Recycle pooled DB connections after this many seconds | #### Highlights ##### Custom library structure Instead of guessing between two folder structures, describe your *exact* library layout in `config.yml`. `{platform}` is the platform folder, the final `{game}` is where a game starts, and anything else in `{braces}` is just a folder you use to organize things. You can override the layout per platform, and give a platform a list of layouts when its games sit at more than one depth. [#&#8203;3571](https://github.com/rommapp/romm/issues/3571) ROM identity is now content-hashed, so you can move files around and keep their saves, favorites, play history and collections. And if you came from Batocera, RetroBat or ES-DE, their platform folder names are recognized out of the box, so most libraries need no mapping at all. [#&#8203;4345](https://github.com/rommapp/romm/issues/4345) ```yaml filesystem: structure: default: "roms/{platform}/{game}" firmware: "bios/{platform}" ps3: "roms/{platform}/{category}/{game}" snes: "roms/{platform}/{region}/{game}" nes: - "roms/{platform}/{game}" - "roms/{platform}/{category}/{game}" ``` **Docs:** [Custom library structure](https://docs.romm.app/5.3.0-beta/getting-started/folder-structure/#custom-library-structure), [Folder name aliases](https://docs.romm.app/5.3.0-beta/platforms/supported-platforms/#folder-name-aliases) ##### Emulator streaming A *massive* update to emulator streaming. You configure one entry per container instead of one per emulator, a platform can pool several containers and grab whichever one is free, and platforms that do both browser and streamed play now get separate *Play* and *Stream* buttons. [#&#8203;4314](https://github.com/rommapp/romm/issues/4314) Saves and save states made while streaming are pulled back into your library with history and thumbnails. Multi-disc games get a disc-swap control, and whichever disc was mounted when you saved is the one remounted when you load. PS2 and GameCube can sync a whole memory card instead of individual save files, with a memory card library to create, rename, version, share and download them. Admins get a desktop session for setting up an emulator with no game running, plus a fleet view to force-release sessions across every container. **Docs:** [Emulator Streaming](https://docs.romm.app/5.3.0-beta/using/emulator-streaming/) ##### Physical games Own a game on a cartridge or disc, but have no ROM for it? You can now add it anyway, by name or by scanning its barcode with your camera, and it gets matched to metadata like any other game. [#&#8203;3848](https://github.com/rommapp/romm/issues/3848) **Docs:** [Physical Games](https://docs.romm.app/5.3.0-beta/using/physical-games/) <img width="522" height="400" alt="Screenshot 2026-09-11 at 6 00 19 PM" src="https://github.com/user-attachments/assets/dae5d22a-5bf5-4603-88a6-f45625ebcb48" /> ##### Walkthroughs Walkthroughs are now documents on a game, sitting next to its manuals. Upload your own, or paste a GameFAQs URL and have it fetched directly (as plain text). It'll even remember where you left off and show a progress bar for each user. [#&#8203;3816](https://github.com/rommapp/romm/issues/3816) **Docs:** [Walkthroughs](https://docs.romm.app/5.3.0-beta/using/walkthroughs/) <img width="1156" height="504" alt="Screenshot 2026-09-11 at 6 03 47 PM" src="https://github.com/user-attachments/assets/5b60009c-bbc6-4b87-8374-e938daf0e362" /> ##### Title IDs read from the binary Scans now pull a game's native Title ID straight out of its binary on PSX, PS2, PS3, PSP, PS Vita, Switch, 3DS, Wii, Wii U, GameCube, Dreamcast, Xbox and Xbox 360. It identifies games on the platforms we don't hash, so a renamed or moved file keeps its saves and collections, and it tells the system where a game writes its saves, so device sync knows what to look for. [#&#8203;3925](https://github.com/rommapp/romm/issues/3925) **Docs:** [Title ids read from the binary](https://docs.romm.app/5.3.0-beta/administration/scanning-and-watcher/#title-ids-read-from-the-binary) ##### Jukebox A soundtrack player for your whole library that follows you as a mini player anywhere in the app. Browse by album, platform, artist or genre, or just put a playlist on: *Free Radio* picks a randomized, album-balanced hour, and there's a *Decade Mix*, recently added, and your personal favorites. [#&#8203;4197](https://github.com/rommapp/romm/issues/4197) **Docs:** [Jukebox](https://docs.romm.app/5.3.0-beta/using/jukebox/) <img width="981" height="609" alt="Screenshot 2026-09-11 at 6 05 57 PM" src="https://github.com/user-attachments/assets/980edde3-51f6-4348-bdaa-a12767181cf3" /> ##### JS-DOS and PICO-8 in the browser Windows 3.x and 9x games now run on `js-dos` and its DOSBox-X backend. Note that `js-dos` saves stay in your browser and are not synced back to the server. [#&#8203;4061](https://github.com/rommapp/romm/issues/4061) PICO-8 cartridges (`.p8` and `.p8.png`) are also now playable, on FAKE-08, with keyboard + mouse, gamepad, or touch input. [#&#8203;4400](https://github.com/rommapp/romm/issues/4400) **Docs:** [js-dos](https://docs.romm.app/5.3.0-beta/using/in-browser-play/js-dos/), [PICO-8](https://docs.romm.app/5.3.0-beta/using/in-browser-play/pico-8/) <img width="676" height="806" alt="Screenshot 2026-09-11 at 6 06 41 PM" src="https://github.com/user-attachments/assets/d1c2e744-e812-4f05-abf6-40f9b6aa51f6" /> ##### Four new metadata sources Steam is now a metadata source for the `win`, `linux` and `mac` platforms, no API key required! It's in the scan, in the **Match ROM** dialog when a PC game was missed or matched to the wrong app, and in the cover picker alongside SteamGridDB. [#&#8203;4241](https://github.com/rommapp/romm/issues/4241) [#&#8203;4498](https://github.com/rommapp/romm/issues/4498) [#&#8203;4500](https://github.com/rommapp/romm/issues/4500) [Demozoo](https://demozoo.org/), [Pouët](https://www.pouet.net/) and [CSDb](https://csdb.dk/) cover demoscene productions on platforms we already support. Tag a file `(demozoo-108)`, or paste an id or a production URL into the ROM editor. [#&#8203;4221](https://github.com/rommapp/romm/issues/4221) ```yaml scan: priority: metadata: - steam # PC platforms only ``` **Docs:** [Metadata Providers](https://docs.romm.app/5.3.0-beta/getting-started/metadata-providers/) ##### EmulatorJS cores and multi-disc games A multi-disc game is handed to EmulatorJS all at once now, so you can swap discs from the emulator's own menu instead of going back and reloading. Set `emulatorjs.disable_batch_bootup` to boot only the disc you launched, if a core mishandles the batch. [#&#8203;3988](https://github.com/rommapp/romm/issues/3988) You can also preselect a core per platform with `emulatorjs.default_cores`. That sets the starting point, and anyone who has already picked a core on their device keeps their choice. [#&#8203;4452](https://github.com/rommapp/romm/issues/4452) **Docs:** [Cores](https://docs.romm.app/5.3.0-beta/using/in-browser-play/emulatorjs/#cores), [Multi-disc games](https://docs.romm.app/5.3.0-beta/using/in-browser-play/emulatorjs/#multi-disc-games) ##### Library-aware recommendations Recommendations are now built from your own library, blending metadata overlap, the list pulled from IGDB, and what you actually play. You'll find them under *Similar games* on a game's page, and in a new *Recommended for you* row on the home screen. Enable it by setting `ENABLE_SCHEDULED_BUILD_RECOMMENDATIONS=true` in your env vars. [#&#8203;4174](https://github.com/rommapp/romm/issues/4174) **Docs:** [Recommendations](https://docs.romm.app/5.3.0-beta/using/recommendations/) <img width="1311" height="369" alt="Screenshot 2026-09-11 at 6 08 32 PM" src="https://github.com/user-attachments/assets/e57942af-6682-4a7c-a6e4-3f04ad3fe5ce" /> ##### Saves A save syncs automatically with the server seconds after its stored in the browser. The player watches the emulator's SRAM while you play and uploads a new version as soon as one appears, so your progress is saved even if the tab is closed or the browser crashes. [#&#8203;4378](https://github.com/rommapp/romm/issues/4378) [#&#8203;4558](https://github.com/rommapp/romm/issues/4558) Saves are also organized into slots, the same model used by the sync clients. Ordinary play goes to the `autosave` slot, which keeps a capped history; a named slot on launch keeps every version. Each version carries a screenshot from the moment it was written, and the server prunes a slot to `MAX_SAVES_PER_SLOT` (50 by default). [#&#8203;4517](https://github.com/rommapp/romm/issues/4517) [#&#8203;4540](https://github.com/rommapp/romm/issues/4540) In-game, the Export and Import save buttons are replaced by a single *Load save or state* picker that restarts the game from the save you pick. [#&#8203;4600](https://github.com/rommapp/romm/issues/4600) [#&#8203;4615](https://github.com/rommapp/romm/issues/4615) ```yaml emulatorjs: auto_save_sync: false # opt ou & Quit ``` **Docs:** [Saves & States](https://docs.romm.app/5.3.0-beta/using/saves-and-states/) ##### Sort and filter by game length You can now sort and filter the gallery by how long a game takes, straight from the gallery rather than having to build a collection for it. It's based on the HowLongToBeat main story time, so it needs `HLTB_API_ENABLED` and a scan that matched the game. [#&#8203;4392](https://github.com/rommapp/romm/issues/4392) **Docs:** [Game length](https://docs.romm.app/5.3.0-beta/using/smart-collections/#game-length) #### Minor changes - feat(v2): link Server Stats platform rows to their platform by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4252](https://github.com/rommapp/romm/pull/4252) - feat(gallery): pick a game's default version by region priority by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4260](https://github.com/rommapp/romm/pull/4260) - feat: split company metadata into publishers and developers by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3898](https://github.com/rommapp/romm/pull/3898) - feat(saves): scope save discovery to a set of ROM IDs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4304](https://github.com/rommapp/romm/pull/4304) - feat(hasheous): make the Hasheous API URL configurable by [@&#8203;closer](https://github.com/closer) in [#&#8203;4300](https://github.com/rommapp/romm/pull/4300) - feat(scan): refresh files scan and uploads into rom folders by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4277](https://github.com/rommapp/romm/pull/4277) - feat(player): confirm before leaving a running game by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4308](https://github.com/rommapp/romm/pull/4308) - feat(v2): make game header tags clickable by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4372](https://github.com/rommapp/romm/pull/4372) - feat(v2): zoom the game cover, and dismiss the lightbox from anywhere on the backdrop by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4373](https://github.com/rommapp/romm/pull/4373) - feat(home): add a game anniversary widget by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4396](https://github.com/rommapp/romm/pull/4396) - feat(firmware): let missing firmware be found, cleaned up, and kept out of the player by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4077](https://github.com/rommapp/romm/pull/4077) - feat(v2): show a reading-progress bar in the manual and walkthrough viewers by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4470](https://github.com/rommapp/romm/pull/4470) - feat(streaming): let the launch screen pick which save to restore by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4507](https://github.com/rommapp/romm/pull/4507) - feat(v2): polish upload actions and pickers across details, player and scan settings by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4576](https://github.com/rommapp/romm/pull/4576) - feat(v2): open the EmulatorJS and js-dos launch views without a page load by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4567](https://github.com/rommapp/romm/pull/4567) - feat(v2): phone-friendly tabs, subtabs and patch picker in game details by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4578](https://github.com/rommapp/romm/pull/4578) - feat(v2): phone-friendly list rows, soundtrack header and mini player by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4580](https://github.com/rommapp/romm/pull/4580) - feat(v2): unify platform/collection settings and fill the viewport by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4618](https://github.com/rommapp/romm/pull/4618) - feat(frontend): tie gallery sorting to URL query params, add a grid sort-axis selector by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4641](https://github.com/rommapp/romm/pull/4641) #### Fixes - fix(v2): Fix missing v2 auth notifications by [@&#8203;Florian-Cullmann](https://github.com/Florian-Cullmann) in [#&#8203;4246](https://github.com/rommapp/romm/pull/4246) - fix(v2): remember the core a game was launched with by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4251](https://github.com/rommapp/romm/pull/4251) - fix(frontend): keep collection ROM counts in step with the gallery by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4261](https://github.com/rommapp/romm/pull/4261) - fix(hltb): keep a pinned HowLongToBeat match through a rescan by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4262](https://github.com/rommapp/romm/pull/4262) - fix(roms): Fix N+1 and entity sort in GET /api/roms by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;4264](https://github.com/rommapp/romm/pull/4264) - fix(auth): send PKCE on the OIDC authorization request by [@&#8203;jgarcesres](https://github.com/jgarcesres) in [#&#8203;4267](https://github.com/rommapp/romm/pull/4267) - fix: Preserve nested arcade ROM filenames by [@&#8203;mvanhorn](https://github.com/mvanhorn) in [#&#8203;4271](https://github.com/rommapp/romm/pull/4271) - fix: map Neo Geo MVS/AES to the RetroAchievements Arcade console by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4282](https://github.com/rommapp/romm/pull/4282) - fix: restore HowLongToBeat lookups and repair endpoint discovery by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4286](https://github.com/rommapp/romm/pull/4286) - fix(scan): let update metadata scans refresh artwork, and record uploads explicitly by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4160](https://github.com/rommapp/romm/pull/4160) - fix(screenscraper): map mega-duck-slash-cougar-boy to ss id 90 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4307](https://github.com/rommapp/romm/pull/4307) - fix(platforms): add doom as a platform with its standalone provider mappings by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4309](https://github.com/rommapp/romm/pull/4309) - fix(roms): stop a racing upload from undoing a folder conversion by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;4316](https://github.com/rommapp/romm/pull/4316) - fix(manual): only ask where a manual goes when the answer is open by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;4315](https://github.com/rommapp/romm/pull/4315) - fix(logs): stop a multi-line record painting over the rows after it by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4303](https://github.com/rommapp/romm/pull/4303) - fix: allow INI files in library scans by [@&#8203;mvanhorn](https://github.com/mvanhorn) in [#&#8203;4328](https://github.com/rommapp/romm/pull/4328) - fix(frontend): Add crossorigin attribute to manifest link so CloudFlare Tunnels does not break site.webmanifest by [@&#8203;Macleykun](https://github.com/Macleykun) in [#&#8203;4332](https://github.com/rommapp/romm/pull/4332) - fix(player): Show a clear message instead of the generic error for threaded cores over non-secure origins by [@&#8203;TRusselo](https://github.com/TRusselo) in [#&#8203;4313](https://github.com/rommapp/romm/pull/4313) - fix(v2): read the date picker's value in UTC, not local time by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4322](https://github.com/rommapp/romm/pull/4322) - fix(backend): Rate limit the unauthenticated metadata heartbeat endpoint by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4327](https://github.com/rommapp/romm/pull/4327) - fix(download): make cached zips readable by nginx and serialize their builds by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;4329](https://github.com/rommapp/romm/pull/4329) - fix(scan): Follow-ups to the sigil title ID branch by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4333](https://github.com/rommapp/romm/pull/4333) - fix(export): write gamelist media where ES-DE and Batocera frontends find it by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4344](https://github.com/rommapp/romm/pull/4344) - fix(retroachievements): don't match ROMs against games with no achievement set by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4352](https://github.com/rommapp/romm/pull/4352) - fix(scan): let a complete rescan clear the ids it rematched by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4353](https://github.com/rommapp/romm/pull/4353) - fix(collections): honor "Show autogenerated collections" on the v2 collections page by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4355](https://github.com/rommapp/romm/pull/4355) - fix(hashing): read zips zipfile can't decode through 7zz by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4357](https://github.com/rommapp/romm/pull/4357) - fix(db): make virtual\_collections and generated columns collation-agnostic by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4374](https://github.com/rommapp/romm/pull/4374) - fix(v2): don't reload the page on reconnect while a game is running by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4369](https://github.com/rommapp/romm/pull/4369) - fix(db): make migrations collation-agnostic on MariaDB by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4371](https://github.com/rommapp/romm/pull/4371) - fix(export): share the ES-DE media folders with the Pegasus export by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4370](https://github.com/rommapp/romm/pull/4370) - fix(auth): require platforms.write and enforce platform visibility on the export endpoints by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4376](https://github.com/rommapp/romm/pull/4376) - fix(export): merge gamelist.xml and metadata.pegasus.txt instead of overwriting by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4368](https://github.com/rommapp/romm/pull/4368) - fix(screenscraper): let the daily-quota breaker recover by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4360](https://github.com/rommapp/romm/pull/4360) - fix(server): default to 4 API workers and fix the WEB\_SERVER\_CONCURRENCY guidance by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4385](https://github.com/rommapp/romm/pull/4385) - fix(gallery): join roms\_metadata before the grouped dedup window by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4390](https://github.com/rommapp/romm/pull/4390) - fix(docker): bump sigil past the zstd asm link failure by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4404](https://github.com/rommapp/romm/pull/4404) - fix(firmware): 404 the content endpoints when the file is gone by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4405](https://github.com/rommapp/romm/pull/4405) - fix(v2): read the webp flag the heartbeat actually sends by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4406](https://github.com/rommapp/romm/pull/4406) - fix(auth): sanitize the username an OIDC provider hands us by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4407](https://github.com/rommapp/romm/pull/4407) - fix(ra): return achievements in RetroAchievements' display order by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4408](https://github.com/rommapp/romm/pull/4408) - fix(v2): keep a click in selection mode from navigating away by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4409](https://github.com/rommapp/romm/pull/4409) - fix(db): recycle pooled connections before the server drops them by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4411](https://github.com/rommapp/romm/pull/4411) - fix(v2): keep the player from booting a rom's media instead of the game by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4412](https://github.com/rommapp/romm/pull/4412) - fix(ss): let region\_mode decide the title, not just the artwork by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4413](https://github.com/rommapp/romm/pull/4413) - fix(scan): tell the clients when SCAN\_TIMEOUT killed the scan by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4414](https://github.com/rommapp/romm/pull/4414) - fix(roms): keep unplayed games when sorting by a rom\_user column by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4424](https://github.com/rommapp/romm/pull/4424) - fix(player): keep RomM chrome off the emulator on mobile by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4425](https://github.com/rommapp/romm/pull/4425) - fix(tasks): match a running task by its registry key by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4430](https://github.com/rommapp/romm/pull/4430) - fix(tasks): refill the Switch TitleDB store a schema bump drops by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4432](https://github.com/rommapp/romm/pull/4432) - fix(frontend): 404 unmatched URLs in v2 instead of "not migrated yet" by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4455](https://github.com/rommapp/romm/pull/4455) - fix(frontend): stop ESLint and Prettier fighting over formatting by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4456](https://github.com/rommapp/romm/pull/4456) - fix(v2): announce the hash chip's disclosure state and action by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4451](https://github.com/rommapp/romm/pull/4451) - fix(v2): give the gallery a real Select all in both layouts by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4453](https://github.com/rommapp/romm/pull/4453) - fix(roms): sort unset per-user and metadata keys last on every engine by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4454](https://github.com/rommapp/romm/pull/4454) - fix(migrations): let every 5.3.0 revision be replayed by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4484](https://github.com/rommapp/romm/pull/4484) - fix(roms): invalidate a user's gallery sidecars on rom\_user writes by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4463](https://github.com/rommapp/romm/pull/4463) - fix(roms): sort grouped galleries by the best sibling's key by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4465](https://github.com/rommapp/romm/pull/4465) - fix(v2): dismiss tooltips when an overlay opens over them by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4482](https://github.com/rommapp/romm/pull/4482) - fix(patcher): apply patches to ROMs inside ZIP archives by [@&#8203;kylebenzo](https://github.com/kylebenzo) in [#&#8203;4472](https://github.com/rommapp/romm/pull/4472) - fix(streaming): prefer the emulator's own screenshot for save-state thumbnails by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4478](https://github.com/rommapp/romm/pull/4478) - fix(tasks): fill the scan counters a job's stored stats predate by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4480](https://github.com/rommapp/romm/pull/4480) - fix(migrations): name the fix when the server denies trigger DDL by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4490](https://github.com/rommapp/romm/pull/4490) - fix(v2): address review findings on the shared metadata provider card by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4516](https://github.com/rommapp/romm/pull/4516) - fix(frontend-v2): center the recommendation caption under the card by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4510](https://github.com/rommapp/romm/pull/4510) - fix(scan): send Playmatch as a metadata source so the UI switch works by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4519](https://github.com/rommapp/romm/pull/4519) - fix(player): stop hiding EmulatorJS's own messages by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4520](https://github.com/rommapp/romm/pull/4520) - fix(collections): pick the current user's favorites collection by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4521](https://github.com/rommapp/romm/pull/4521) - fix(player): never boot a multi-disc playlist on its own by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4524](https://github.com/rommapp/romm/pull/4524) - fix(player): give a gamepad a way onto the Play button by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4525](https://github.com/rommapp/romm/pull/4525) - fix(v2): compare typed confirmations on the rendered phrase by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4523](https://github.com/rommapp/romm/pull/4523) - fix(v2): remove double border on selected game cards by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4547](https://github.com/rommapp/romm/pull/4547) - fix(v2): make the Missing games selection actionable by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4527](https://github.com/rommapp/romm/pull/4527) - fix(v2): stop offering a missing rom's files for download by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4528](https://github.com/rommapp/romm/pull/4528) - fix(player): attach a real screenshot to a manual save state by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4526](https://github.com/rommapp/romm/pull/4526) - fix(tasks): refuse a manual task run when no worker is listening by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4548](https://github.com/rommapp/romm/pull/4548) - fix(tasks): read older cleanup stats, and check the worker where it enqueues by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4551](https://github.com/rommapp/romm/pull/4551) - fix: treat every disc descriptor as a disc, not just .cue by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4532](https://github.com/rommapp/romm/pull/4532) - fix(v2): jukebox in the settings sidebar, launch-view titles, match overlay by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4555](https://github.com/rommapp/romm/pull/4555) - fix(v2): make the player back links and the details Play button real links by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4560](https://github.com/rommapp/romm/pull/4560) - fix(streaming): pool same-origin webstation containers and keep a claim on its own container by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4533](https://github.com/rommapp/romm/pull/4533) - fix(scan): don't report an unanswered lookup as a confirmed miss by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4534](https://github.com/rommapp/romm/pull/4534) - fix(streaming): make broker save-clearing configurable and date the picker by its sort key by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;4542](https://github.com/rommapp/romm/pull/4542) - fix(tools): repair the test-data seeder's imports and rom digests by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4554](https://github.com/rommapp/romm/pull/4554) - fix(v2): stop the layout's min-height leaking into the gallery shell by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4563](https://github.com/rommapp/romm/pull/4563) - fix(v2): line the placeholder rows up with the rows they stand in for by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4561](https://github.com/rommapp/romm/pull/4561) - fix(v2): invoke the view transition callback once by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4574](https://github.com/rommapp/romm/pull/4574) - fix(v2): stop probing platform icon variants that cannot exist by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4573](https://github.com/rommapp/romm/pull/4573) - fix(v2): stop the unhandled rejection when a view transition is preempted by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4570](https://github.com/rommapp/romm/pull/4570) - fix(scan): read PSX and PS2 serials sigil missed and identify multi-disc roms by their first disc by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;4566](https://github.com/rommapp/romm/pull/4566) - fix(deps): bump engine.io-client past the ws memory-exhaustion advisory by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4571](https://github.com/rommapp/romm/pull/4571) - fix(v2): close the launch-path gaps found by the pr-ready gauntlet by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4579](https://github.com/rommapp/romm/pull/4579) - fix(frontend): restate the player isolation URLs instead of reading nginx by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4583](https://github.com/rommapp/romm/pull/4583) - fix(about): show current git branch instead of "development" by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4617](https://github.com/rommapp/romm/pull/4617) - fix(roms): answer the id endpoints without building every Rom by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4584](https://github.com/rommapp/romm/pull/4584) - fix(backend): return identifiers without building an ORM row per id by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4586](https://github.com/rommapp/romm/pull/4586) - fix(roms): read note authors off the joined user by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4588](https://github.com/rommapp/romm/pull/4588) - fix(collections): project identifier columns instead of loading whole collections by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4589](https://github.com/rommapp/romm/pull/4589) - fix(roms): load the parent rom with a rom file so the file schema validates by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4593](https://github.com/rommapp/romm/pull/4593) - fix(v2): UI/UX polish: unified empty states, smoother loading, tab query cleanup by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4594](https://github.com/rommapp/romm/pull/4594) - fix(gallery): hold the v2 list-view columns still while rows load by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4598](https://github.com/rommapp/romm/pull/4598) - fix(v2): drop the box behind platform icons in the list row by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4601](https://github.com/rommapp/romm/pull/4601) - fix(v2): right-align the list-view quantity columns by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4603](https://github.com/rommapp/romm/pull/4603) - fix(v2): align HLTB column values across the strip by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4604](https://github.com/rommapp/romm/pull/4604) - fix(v2): restore blurred glass off Safari and guard the unguarded subgrid by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4607](https://github.com/rommapp/romm/pull/4607) - fix(upload): accept empty ROM files by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4610](https://github.com/rommapp/romm/pull/4610) - fix(upload): refuse an empty file that declares chunks by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4611](https://github.com/rommapp/romm/pull/4611) - fix(upload): style the pending-files Add button as a primary add action by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4612](https://github.com/rommapp/romm/pull/4612) - fix(covers): keep animated covers animated after they are applied by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4613](https://github.com/rommapp/romm/pull/4613) - fix(states): fold core groups from the whole header, center card actions by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4614](https://github.com/rommapp/romm/pull/4614) - fix(covers): rework the search cover dialog layout by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4616](https://github.com/rommapp/romm/pull/4616) - fix(i18n): route hard-coded UI strings through the locale files by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4619](https://github.com/rommapp/romm/pull/4619) - fix(a11y): translate the v2 primitives' own control labels by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4620](https://github.com/rommapp/romm/pull/4620) - fix(upload): mask a hidden platform on the chunked upload start by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4633](https://github.com/rommapp/romm/pull/4633) - fix(patcher): budget a zip member as it is extracted by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4634](https://github.com/rommapp/romm/pull/4634) - fix(security): keep CSRF in force when a session cookie is present by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4648](https://github.com/rommapp/romm/pull/4648) #### Other changes - chore(agents): add a review-polish skill from recurring PR corrections by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4250](https://github.com/rommapp/romm/pull/4250) - chore(deps): bump starlette from 1.0.1 to 1.6.0 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4254](https://github.com/rommapp/romm/pull/4254) - refactor(v2): give the metadata provider taxonomy one home by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4263](https://github.com/rommapp/romm/pull/4263) - chore(deps-dev): Bump brace-expansion from 2.0.3 to 2.1.4 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4266](https://github.com/rommapp/romm/pull/4266) - chore(deps): Bump asyncssh from 2.23.0 to 2.23.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4292](https://github.com/rommapp/romm/pull/4292) - refactor(v2): extract a shared player shell by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4301](https://github.com/rommapp/romm/pull/4301) - refactor(assets): make rom\_ids the only ROM scope on saves and states by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4305](https://github.com/rommapp/romm/pull/4305) - docs(skills): merge pre-pr-verification into review-polish by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4311](https://github.com/rommapp/romm/pull/4311) - chore(deps): Bump tornado from 6.5.7 to 6.5.8 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4324](https://github.com/rommapp/romm/pull/4324) - chore(deps-dev): Bump postcss-selector-parser from 7.1.1 to 7.1.5 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4325](https://github.com/rommapp/romm/pull/4325) - chore(deps-dev): Bump browserslist from 4.28.1 to 4.28.8 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4326](https://github.com/rommapp/romm/pull/4326) - chore(deps-dev): Bump fast-uri from 3.1.5 to 3.1.7 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4330](https://github.com/rommapp/romm/pull/4330) - chore(deps-dev): Bump [@&#8203;humanfs/node](https://github.com/humanfs/node) from 0.16.7 to 0.16.8 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4331](https://github.com/rommapp/romm/pull/4331) - chore: drop the vcrpy exclude-newer override by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4334](https://github.com/rommapp/romm/pull/4334) - refactor(tasks): schedule with RQ instead of rq-scheduler by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4274](https://github.com/rommapp/romm/pull/4274) - docs(agents): never run the full backend test suite locally by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4336](https://github.com/rommapp/romm/pull/4336) - perf(scan): give scans their own queue and worker by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4275](https://github.com/rommapp/romm/pull/4275) - ci: Bump GitHub Actions to Node 24 runtimes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4341](https://github.com/rommapp/romm/pull/4341) - chore(deps): Bump cryptography from 49.0.0 to 50.0.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4342](https://github.com/rommapp/romm/pull/4342) - perf(scan): coalesce scan progress reports, and give the rom refetch one home by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4340](https://github.com/rommapp/romm/pull/4340) - chore(dev): move the Authentik stack into docker-compose.oidc.yml by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4367](https://github.com/rommapp/romm/pull/4367) - chore(python): move the backend to Python 3.14 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4359](https://github.com/rommapp/romm/pull/4359) - perf(tests): stop the suite waiting on connections it never wanted by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4375](https://github.com/rommapp/romm/pull/4375) - perf(backend): add missing gallery indexes and reconcile model/schema index drift by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4384](https://github.com/rommapp/romm/pull/4384) - perf(scan): read files in 256 KiB chunks instead of 8 KiB by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4387](https://github.com/rommapp/romm/pull/4387) - perf(scan): default to 4 scan workers instead of 1 by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4386](https://github.com/rommapp/romm/pull/4386) - chore(tasks): Quiet RQ per-tick scheduler heartbeat log line by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4393](https://github.com/rommapp/romm/pull/4393) - chore(deps-dev): bump vitest from 4.1.8 to 4.1.11 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4399](https://github.com/rommapp/romm/pull/4399) - chore(deps-dev): bump js-yaml from 4.3.1 to 4.3.2 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4401](https://github.com/rommapp/romm/pull/4401) - security: strip control characters from names reaching the mod\_zip manifest by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4402](https://github.com/rommapp/romm/pull/4402) - perf(gallery): materialize the ids sibling\_roms matches on by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4394](https://github.com/rommapp/romm/pull/4394) - chore(agents): Add a pr-ready skill chaining the four PR review passes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4415](https://github.com/rommapp/romm/pull/4415) - perf(metadata): stop storing the metadata dumps twice over in the cache by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4416](https://github.com/rommapp/romm/pull/4416) - perf(metadata): compress the cache-only metadata dump records by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4426](https://github.com/rommapp/romm/pull/4426) - docs: add a one-click deploys section to the README by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4427](https://github.com/rommapp/romm/pull/4427) - chore(deps): upgrade rq to 2.12 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4428](https://github.com/rommapp/romm/pull/4428) - ci: align and manage the Docker service image pins by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4433](https://github.com/rommapp/romm/pull/4433) - docs: tighten the README copy by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4438](https://github.com/rommapp/romm/pull/4438) - ci: build each arch on a native runner and cache layers by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4437](https://github.com/rommapp/romm/pull/4437) - chore(deps): update astral-sh/setup-uv action to v10.1.0 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4440](https://github.com/rommapp/romm/pull/4440) - chore(deps): update postgres docker tag to v18 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4444](https://github.com/rommapp/romm/pull/4444) - chore(deps): update github actions (major) by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4445](https://github.com/rommapp/romm/pull/4445) - ci: pin the amd64 image build to ubuntu-24.04 by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4458](https://github.com/rommapp/romm/pull/4458) - ci: run trunk check on master pushes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4457](https://github.com/rommapp/romm/pull/4457) - chore(deps): update ghcr.io/goauthentik/server docker tag to v2024.12.5 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4441](https://github.com/rommapp/romm/pull/4441) - docs(examples): drop the gc override from the config example by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4461](https://github.com/rommapp/romm/pull/4461) - chore(ci): drop the docs and website release triggers by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4491](https://github.com/rommapp/romm/pull/4491) - perf(migrations): add the 5.3.0 roms columns in one table copy by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4501](https://github.com/rommapp/romm/pull/4501) - perf(docker): upgrade nginx to 1.29.8 and tune the serving layer by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4468](https://github.com/rommapp/romm/pull/4468) - docs: correct stale Vuetify claims in v2 skills and architecture doc by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4474](https://github.com/rommapp/romm/pull/4474) - chore(translation): translate the remaining untranslated zh\_CN strings by [@&#8203;wolfg1969](https://github.com/wolfg1969) in [#&#8203;4489](https://github.com/rommapp/romm/pull/4489) - refactor(roms): give the ROM filters a single home, and match them against the facets mirror by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4487](https://github.com/rommapp/romm/pull/4487) - ci: gate the preview-build report job on the build-preview label by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4475](https://github.com/rommapp/romm/pull/4475) - perf(frontend): precompress built assets and serve them with gzip\_static by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4471](https://github.com/rommapp/romm/pull/4471) - chore(deps): update github actions by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4513](https://github.com/rommapp/romm/pull/4513) - chore(uv): bump to 0.12.13 and make pyproject the single source for the version by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4469](https://github.com/rommapp/romm/pull/4469) - refactor(v2): unify metadata provider cards behind a shared component by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4515](https://github.com/rommapp/romm/pull/4515) - refactor(frontend): native fullscreen where it exists, one path for all players by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4514](https://github.com/rommapp/romm/pull/4514) - refactor(player): route the mount-time Play focus through the shared check by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4538](https://github.com/rommapp/romm/pull/4538) - chore(deps): update docker/setup-buildx-action action to v4.4.1 by [@&#8203;renovate](https://github.com/renovate)\[bot] in [#&#8203;4544](https://github.com/rommapp/romm/pull/4544) - style(player): one presentation for save and state facts by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4545](https://github.com/rommapp/romm/pull/4545) - docs: require screenshots and mermaid diagrams in PR descriptions by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4549](https://github.com/rommapp/romm/pull/4549) - chore(uploads): manual file uploads into a ROM across the Files and Media tabs by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4550](https://github.com/rommapp/romm/pull/4550) - chore(deps): bump nanoid from 3.3.16 to 3.3.19 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4530](https://github.com/rommapp/romm/pull/4530) - chore(deps-dev): bump [@&#8203;babel/core](https://github.com/babel/core) from 7.29.0 to 7.29.7 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4531](https://github.com/rommapp/romm/pull/4531) - docs: upload PR screenshots with gh --attach by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4625](https://github.com/rommapp/romm/pull/4625) - chore(frontend): upgrade vue-tsc to 3.x and raise the TypeScript floor by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4622](https://github.com/rommapp/romm/pull/4622) - chore(deps): bump md-editor-v3 from 5.8.5 to 6.5.4 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4627](https://github.com/rommapp/romm/pull/4627) - chore(storybook): light and dark theme switch work in storybook by [@&#8203;andest01](https://github.com/andest01) in [#&#8203;4626](https://github.com/rommapp/romm/pull/4626) - docs(claude): add a knowledge-base skill for Greptile's repo docs by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;4595](https://github.com/rommapp/romm/pull/4595) - perf(backend): resolve roms without the related load in the file and props routes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4590](https://github.com/rommapp/romm/pull/4590) - perf(backend): check rom-note visibility without the related load by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4587](https://github.com/rommapp/romm/pull/4587) - test(backend): stop the suite emitting warnings by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4597](https://github.com/rommapp/romm/pull/4597) - docs(files): state what actually guards an inline HTML document by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4636](https://github.com/rommapp/romm/pull/4636) - style: apply the formatting requires-python already implies by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4638](https://github.com/rommapp/romm/pull/4638) - docs: drop the Aikido badge from the readme by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4659](https://github.com/rommapp/romm/pull/4659) #### New Contributors - [@&#8203;jgarcesres](https://github.com/jgarcesres) made their first contribution in [#&#8203;4267](https://github.com/rommapp/romm/pull/4267) - [@&#8203;bphenriques](https://github.com/bphenriques) made their first contribution in [#&#8203;4061](https://github.com/rommapp/romm/pull/4061) - [@&#8203;axelquack](https://github.com/axelquack) made their first contribution in [#&#8203;4221](https://github.com/rommapp/romm/pull/4221) - [@&#8203;closer](https://github.com/closer) made their first contribution in [#&#8203;4300](https://github.com/rommapp/romm/pull/4300) - [@&#8203;Macleykun](https://github.com/Macleykun) made their first contribution in [#&#8203;4332](https://github.com/rommapp/romm/pull/4332) - [@&#8203;TRusselo](https://github.com/TRusselo) made their first contribution in [#&#8203;4313](https://github.com/rommapp/romm/pull/4313) - [@&#8203;kylebenzo](https://github.com/kylebenzo) made their first contribution in [#&#8203;4472](https://github.com/rommapp/romm/pull/4472) - [@&#8203;andest01](https://github.com/andest01) made their first contribution in [#&#8203;4626](https://github.com/rommapp/romm/pull/4626) **Full Changelog**: <https://github.com/rommapp/romm/compare/5.2.0...5.3.0> ### [`v5.2.0`](https://github.com/rommapp/romm/releases/tag/5.2.0) [Compare Source](https://github.com/rommapp/romm/compare/5.1.0...5.2.0) #### Minor changes - feat(screenscraper): pace and report scans against the account's reported limits by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3989](https://github.com/rommapp/romm/pull/3989) - feat: reorderable region and language priority lists in scan settings by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4015](https://github.com/rommapp/romm/pull/4015) - feat(frontend): autofocus the search field on the Search view by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4022](https://github.com/rommapp/romm/pull/4022) - feat(home): make the random pick widget clickable and denser by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4056](https://github.com/rommapp/romm/pull/4056) - feat(rating): show provider ratings by [@&#8203;wadiebs](https://github.com/wadiebs) in [#&#8203;4096](https://github.com/rommapp/romm/pull/4096) - feat(soundtrack player): improve artwork by [@&#8203;wadiebs](https://github.com/wadiebs) in [#&#8203;4168](https://github.com/rommapp/romm/pull/4168) - feat(v2): previous / next game buttons on the details page by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4012](https://github.com/rommapp/romm/pull/4012) - feat(v2): search platforms by slug, folder, category and family by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4136](https://github.com/rommapp/romm/pull/4136) - feat(hasheous): map famicom to the NES platform by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4162](https://github.com/rommapp/romm/pull/4162) - feat(v2): show IGDB ports on the game overview by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4184](https://github.com/rommapp/romm/pull/4184) #### Fixes - fix(steamgriddb): skip DMCA-locked grids when scraping SteamGridDB by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4004](https://github.com/rommapp/romm/pull/4004) - fix(security): close three permission gaps found auditing 5.1.0 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3993](https://github.com/rommapp/romm/pull/3993) - fix(backend): re-run the Hasheous hash lookup on a hashes scan by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4013](https://github.com/rommapp/romm/pull/4013) - fix(v2): keep RSelect menus inside the viewport and tidy the selection text by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4014](https://github.com/rommapp/romm/pull/4014) - fix(roms): give each FPKGi package a unique name within its rom by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4021](https://github.com/rommapp/romm/pull/4021) - fix(backend): scope ScreenScraper French fallback to taxonomy fields by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4026](https://github.com/rommapp/romm/pull/4026) - fix(v2): pivot to a filtered search from the overview info grid by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4023](https://github.com/rommapp/romm/pull/4023) - fix(backend): tolerate calendar-invalid gamelist dates on PostgreSQL by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4024](https://github.com/rommapp/romm/pull/4024) - fix(player): skip the save load a state immediately discards by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4025](https://github.com/rommapp/romm/pull/4025) - fix(roms): search the gallery by CRC32, MD5, SHA-1 and RA hash by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4032](https://github.com/rommapp/romm/pull/4032) - fix(docker): always start the RQ scheduler by [@&#8203;XenuIsWatching](https://github.com/XenuIsWatching) in [#&#8203;4033](https://github.com/rommapp/romm/pull/4033) - fix(backend): scope the kiosk read-only cap to the anonymous visitor by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4047](https://github.com/rommapp/romm/pull/4047) - fix: batch of open bug fixes ([#&#8203;4027](https://github.com/rommapp/romm/issues/4027), [#&#8203;4034](https://github.com/rommapp/romm/issues/4034), [#&#8203;4035](https://github.com/rommapp/romm/issues/4035), [#&#8203;4038](https://github.com/rommapp/romm/issues/4038), [#&#8203;4040](https://github.com/rommapp/romm/issues/4040), [#&#8203;4043](https://github.com/rommapp/romm/issues/4043), [#&#8203;4045](https://github.com/rommapp/romm/issues/4045)) by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4051](https://github.com/rommapp/romm/pull/4051) - fix(scan): report only newly discovered firmware by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4055](https://github.com/rommapp/romm/pull/4055) - fix(rom-details): render the manual viewer on mobile by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4058](https://github.com/rommapp/romm/pull/4058) - fix(hasheous): read the CHD and DOS signature sources by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4060](https://github.com/rommapp/romm/pull/4060) - fix(stats): pluralize the platform game count and lift meta legibility by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4065](https://github.com/rommapp/romm/pull/4065) - fix(config): map tic80 to the tic-80 platform slug by [@&#8203;XenuIsWatching](https://github.com/XenuIsWatching) in [#&#8203;4090](https://github.com/rommapp/romm/pull/4090) - fix(docker): keep the Redis password out of the RQ worker command line by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4093](https://github.com/rommapp/romm/pull/4093) - fix(v2): make hashes recoverable without HTTPS and show all firmware hashes by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4083](https://github.com/rommapp/romm/pull/4083) - fix(home): stop scanning the library filesystem on every home page load by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4069](https://github.com/rommapp/romm/pull/4069) - fix(auth): accept a stale user-bound CSRF token on anonymous requests by [@&#8203;bikeborb](https://github.com/bikeborb) in [#&#8203;4086](https://github.com/rommapp/romm/pull/4086) - fix(v2): drop a random pick after leaving the platform by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4113](https://github.com/rommapp/romm/pull/4113) - fix(states): repoint the row when a re-upload changes emulator by [@&#8203;TowyTowy](https://github.com/TowyTowy) in [#&#8203;4112](https://github.com/rommapp/romm/pull/4112) - fix(v2): keep Playmatch selectable when providers are set to All by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4120](https://github.com/rommapp/romm/pull/4120) - fix(hashing): extract CHD SHA-1 for folder-based ROMs by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4129](https://github.com/rommapp/romm/pull/4129) - fix(v2): compute game-card cover width instead of deriving it by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4135](https://github.com/rommapp/romm/pull/4135) - fix(media): stop recording paths for media that never landed by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4131](https://github.com/rommapp/romm/pull/4131) - fix(screenscraper): report rejected credentials instead of a bare 403 by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4130](https://github.com/rommapp/romm/pull/4130) - fix(launchbox): match ROMs during a scan the way Match ROM does by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4121](https://github.com/rommapp/romm/pull/4121) - fix(hltb): renew rejected HLTB sessions instead of failing the scan by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4125](https://github.com/rommapp/romm/pull/4125) - fix(v2): drop a random pick that lands after leaving the gallery by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4138](https://github.com/rommapp/romm/pull/4138) - fix(ss): store the ScreenScraper box-2D front locally by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4139](https://github.com/rommapp/romm/pull/4139) - fix(v2): show a running scan on pages loaded mid-scan by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4142](https://github.com/rommapp/romm/pull/4142) - fix(resources): fetch each cover once instead of once per size by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4143](https://github.com/rommapp/romm/pull/4143) - fix(metadata): identify archives and folder ROMs by the right file by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4146](https://github.com/rommapp/romm/pull/4146) - fix(match-rom): show the saving overlay above the grid match panel by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4147](https://github.com/rommapp/romm/pull/4147) - refactor(i18n): name gallery filters by property instead of "Show X" by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4149](https://github.com/rommapp/romm/pull/4149) - fix(v2): bulk favorite no-ops on a fresh instance and reports the wrong direction by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4151](https://github.com/rommapp/romm/pull/4151) - fix: refresh the v2 gallery when a ROM is edited or matched by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4153](https://github.com/rommapp/romm/pull/4153) - fix(v2): pin mobile bottom nav with sticky instead of fixed by [@&#8203;nickybmon](https://github.com/nickybmon) in [#&#8203;4155](https://github.com/rommapp/romm/pull/4155) - fix(export): stop miximage\_v2 colliding with miximage, persist gamelist back covers by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4156](https://github.com/rommapp/romm/pull/4156) - fix(hashing): surface unreadable zip and tar archives by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4163](https://github.com/rommapp/romm/pull/4163) - fix: remove double URL-encoding of MobyGames search terms by [@&#8203;justinjd00](https://github.com/justinjd00) in [#&#8203;4181](https://github.com/rommapp/romm/pull/4181) - fix(i18n): correct Breton pt\_BR player-count and align ja\_JP/tr\_TR by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4183](https://github.com/rommapp/romm/pull/4183) - fix(i18n): translate pt\_BR strings left in English by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4185](https://github.com/rommapp/romm/pull/4185) - fix(v2): close open overlays when the route changes by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4193](https://github.com/rommapp/romm/pull/4193) - Changed default compose, will explain more in pull by [@&#8203;danblu3](https://github.com/danblu3) in [#&#8203;4202](https://github.com/rommapp/romm/pull/4202) - fix(v2): drop the roms from the gallery when they leave the collection on screen by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4204](https://github.com/rommapp/romm/pull/4204) - fix(scan): normalize region and language tags to one canonical spelling by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4206](https://github.com/rommapp/romm/pull/4206) - fix: demote uvicorn HTTP access logs to WARNING/DEBUG only by [@&#8203;gantoine](https://github.com/gantoine) with [@&#8203;Copilot](https://github.com/Copilot) in [#&#8203;4214](https://github.com/rommapp/romm/pull/4214) - fix: Add `.xdelta` support to server-side ROM patcher and v2 patcher UI by [@&#8203;gantoine](https://github.com/gantoine) with [@&#8203;Copilot](https://github.com/Copilot) in [#&#8203;4215](https://github.com/rommapp/romm/pull/4215) - fix(states): enforce ROM visibility when uploading a state by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4216](https://github.com/rommapp/romm/pull/4216) - fix(auth): build invite links from ROMM\_BASE\_URL by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4217](https://github.com/rommapp/romm/pull/4217) - fix(scan): re-read filename tags on a complete rescan by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4218](https://github.com/rommapp/romm/pull/4218) - fix(libretro): cache missing thumbnail listings by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4219](https://github.com/rommapp/romm/pull/4219) - fix(launchbox): match titles differing only by punctuation by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4224](https://github.com/rommapp/romm/pull/4224) - fix(hltb): match titles whose series prefix the catalogue omits by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4225](https://github.com/rommapp/romm/pull/4225) - fix(ss): don't use the Switch icon URL as the manual URL by [@&#8203;TowyTowy](https://github.com/TowyTowy) in [#&#8203;4228](https://github.com/rommapp/romm/pull/4228) - fix(resources): publish downloaded resources atomically by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4230](https://github.com/rommapp/romm/pull/4230) - fix(i18n): translate route titles once locale messages are loaded by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4231](https://github.com/rommapp/romm/pull/4231) - fix(i18n): add the missing playlists permission entity label by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4234](https://github.com/rommapp/romm/pull/4234) - fix(hltb): fetch metadata when a HowLongToBeat ID is set by hand by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4235](https://github.com/rommapp/romm/pull/4235) - fix(launchbox): match LaunchBox dumps that separate words with underscores by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4239](https://github.com/rommapp/romm/pull/4239) - fix(backend): make filters match against all grouped versions instead of only the main version of each game entry by [@&#8203;darkpaul91](https://github.com/darkpaul91) in [#&#8203;4240](https://github.com/rommapp/romm/pull/4240) - feat(metadata): warn when ScreenScraper dev credentials are missing by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;4243](https://github.com/rommapp/romm/pull/4243) - fix(scan): ignore Windows Zone.Identifier files by [@&#8203;Florian-Cullmann](https://github.com/Florian-Cullmann) in [#&#8203;4245](https://github.com/rommapp/romm/pull/4245) #### Other changes - perf(roms): stop the Missing tab scanning the whole library by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4005](https://github.com/rommapp/romm/pull/4005) - perf(scan): stop hashing single-file ROMs twice by [@&#8203;XenuIsWatching](https://github.com/XenuIsWatching) in [#&#8203;4017](https://github.com/rommapp/romm/pull/4017) - perf(collections): serve smart and standard collections from a composed query by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4039](https://github.com/rommapp/romm/pull/4039) - perf(roms): extend the sibling covering index to cover the ROM grouping query by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4054](https://github.com/rommapp/romm/pull/4054) - perf(roms): let a gallery window fetch skip the library count by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4062](https://github.com/rommapp/romm/pull/4062) - perf(roms): pick a random rom without paging to a random offset by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4071](https://github.com/rommapp/romm/pull/4071) - perf(roms): sort metadata fields on the indexed roms column by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4078](https://github.com/rommapp/romm/pull/4078) - perf(gallery): resolve the random rom button in one request by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4097](https://github.com/rommapp/romm/pull/4097) - perf(home): stop counting the whole library for the two home rows by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;4110](https://github.com/rommapp/romm/pull/4110) - perf(scan): scope a rom-id scan to the selected roms by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4188](https://github.com/rommapp/romm/pull/4188) - perf(scan): cut scan memory and drop a quadratic file listing check by [@&#8203;XenuIsWatching](https://github.com/XenuIsWatching) in [#&#8203;4198](https://github.com/rommapp/romm/pull/4198) - perf(scan): stop re-hashing unchanged firmware on every scan by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4211](https://github.com/rommapp/romm/pull/4211) - test: fix flaky LaunchBox release date property test (DST fold) by [@&#8203;sdornan](https://github.com/sdornan) in [#&#8203;4242](https://github.com/rommapp/romm/pull/4242) - chore(deps): bump aiohttp from 3.14.1 to 3.14.3 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4115](https://github.com/rommapp/romm/pull/4115) - chore(deps-dev): bump fast-uri from 3.1.4 to 3.1.5 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4118](https://github.com/rommapp/romm/pull/4118) - chore(deps): bump socket.io-parser from 4.2.6 to 4.2.7 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4119](https://github.com/rommapp/romm/pull/4119) - chore(deps-dev): bump js-yaml from 4.2.0 to 4.3.1 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;4189](https://github.com/rommapp/romm/pull/4189) > \[!NOTE] > > ### API changes > > | Change | Description | > | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | > | `GET /roms/random` | Retrieve one rom picked at random, or `null` when the scope holds none. Accepts `platform_ids`, `collection_id`, `virtual_collection_id` and `smart_collection_id`. | > | `GET /roms` | Now accepts a `with_total` parameter (default `true`). Set it to `false` when the caller already knows the total; `total` then comes back `null` unless the rom id index is being built and already carries it. | > | `GET /setup/library` | `existing_platforms` now comes back empty once the database holds roms, instead of walking every platform directory. | > | `POST /states` | Now rejects an upload against a rom the caller cannot see. | > | `DELETE /roms/{rom_id}/files/{file_id}` | Now requires the `ROMS`/`DELETE` grant instead of `ROMS`/`WRITE`. | > | `GET /feeds/fpkgi` | Package names are deduplicated within a rom, so two packages that share a title id no longer overwrite each other on the console. | > | `GET /collections/smart/{id}` | The cached `rom_ids`, `rom_count` and covers are now resolved as the collection owner, not the requesting viewer. The live rom listing is still per-requester. | > | `CustomLimitOffsetPage[SimpleRomSchema]` | `total` is now nullable. | > | `InviteLinkSchema` | Adds `url`, the full registration link built from `ROMM_BASE_URL`, or `null` when that points at loopback. | > | `MetadataSourcesDict` | Adds `SS_DEV_CREDENTIALS_SET`. | > | `RomSSMetadata` | Adds `box2d_path`. | > | `RomGamelistMetadata` | Adds `box2d_back_path`, `fanart_path` and `title_screen_path`. | > | `RomHasheousMetadata` | Adds `mame_redump_match`. | > > #### Streaming endpoints > > These moved from `ROMS_READ` to `ROMS_USER_WRITE`, which is always-on for authenticated users but absent from the read-only set a kiosk visitor gets. #### New Contributors - [@&#8203;wadiebs](https://github.com/wadiebs) made their first contribution in [#&#8203;4096](https://github.com/rommapp/romm/pull/4096) - [@&#8203;sdornan](https://github.com/sdornan) made their first contribution in [#&#8203;4120](https://github.com/rommapp/romm/pull/4120) - [@&#8203;nickybmon](https://github.com/nickybmon) made their first contribution in [#&#8203;4155](https://github.com/rommapp/romm/pull/4155) - [@&#8203;justinjd00](https://github.com/justinjd00) made their first contribution in [#&#8203;4181](https://github.com/rommapp/romm/pull/4181) - [@&#8203;danblu3](https://github.com/danblu3) made their first contribution in [#&#8203;4202](https://github.com/rommapp/romm/pull/4202) - [@&#8203;darkpaul91](https://github.com/darkpaul91) made their first contribution in [#&#8203;4240](https://github.com/rommapp/romm/pull/4240) - [@&#8203;Florian-Cullmann](https://github.com/Florian-Cullmann) made their first contribution in [#&#8203;4245](https://github.com/rommapp/romm/pull/4245) **Full Changelog**: <https://github.com/rommapp/romm/compare/5.1.0...5.2.0> ### [`v5.1.0`](https://github.com/rommapp/romm/releases/tag/5.1.0) [Compare Source](https://github.com/rommapp/romm/compare/5.0.0...5.1.0) > \[!WARNING] > When binary logging is on and the connecting user lacks `SUPER`, trigger DDL is refused unless the global `log_bin_trust_function_creators flag` is 1. Run this on your MariaDB as an admin/root user (not the romm app user): > > ```sql > SET GLOBAL log_bin_trust_function_creators = 1; > ``` > > `SET GLOBAL` is lost on DB restart. Either add to your MariaDB `my.cnf` under `[mysqld]`: > > ```cnf > log_bin_trust_function_creators = 1 > ``` > > Or grant the app user the privilege (MariaDB 10.5+): > > ```sql > `GRANT BINLOG ADMIN ON *.* TO 'romm'@&#8203;'%';` -- or: GRANT SUPER ON *.* TO 'romm'@&#8203;'%'; > ``` > \[!WARNING] > If your reverse proxy caches responses itself, or overrides/strips `Cache-Control`, it will defeat our built-in cache and serve a stale `index.html` that points at bundles the new image no longer ships, which usually shows up as a blank page or a broken UI after upgrading. After upgrading, purge your proxy's or CDN's existing cache once so clients stop being served entries stored under the old (header-less) behaviour. #### Highlights ##### Emulator streaming Launches a game into a **native** emulator running in a separate container and streams the picture, sound, and input back to your browser. Unlike EmulatorJS, the emulation runs server-side on real emulator binaries (PCSX2, Dolphin, Xemu), so the heavy lifting happens on the host rather than in the client. [#&#8203;3211](https://github.com/rommapp/romm/issues/3211) > \[!IMPORTANT] > Emulator streaming is in early development, and documentation is coming soon! If you *really* want to try it today, refer to [this pull request](https://github.com/rommapp/docs/pull/99) in the `rommapp/docs` repo. <img width="1310" height="797" alt="image2" src="https://github.com/user-attachments/assets/5b4b3f4a-8f31-49aa-aaa9-9fb79fd54cb7" /> ##### Per-field artwork priority overrides Optional per-field artwork priority to prioritize metadata sources differently for covers, screenshots, and manuals, which fall back to the shared `scan.priority.artwork`. [#&#8203;3838](https://github.com/rommapp/romm/issues/3838) ```yaml scan: priority: cover: # Cover art only - igdb - ss screenshot: # Screenshots only - ss - igdb manual: # Game manuals only - launchbox region_mode: prefer_rom_tags Region tags win over priority list ``` ##### Scan settings UI Brand new UI to edit the whole `scan.*` section of `config.yml`. [#&#8203;3854](https://github.com/rommapp/romm/issues/3854) <img width="1228" height="514" alt="Screenshot 2026-07-21 at 8 20 20 PM" src="https://github.com/user-attachments/assets/18da25cb-4f56-4e06-b3a7-7e5cdfc60b37" /> ##### Manual cover search filters Client-side filters to the manual cover-search dialog to quickly narrow SteamGridDB results instead of scrolling through dozens of community covers. [#&#8203;3857](https://github.com/rommapp/romm/issues/3857) <img width="867" height="177" alt="Screenshot 2026-07-21 at 8 20 44 PM" src="https://github.com/user-attachments/assets/ddc4c090-2147-49da-a39d-63f474dd00b0" /> #### Minor changes * feat: add file deletion to the Files tab on the game details page by @&#8203;zurdi15 with @&#8203;Copilot in https://github.com/rommapp/romm/pull/3753 * feat(v2): add reduced-effects mode for low-power devices by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3766 * feat(v2): link Hasheous provider to game detail page by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3783 * feat: add soundtrack gallery filter by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3790 * feat(playmatch): support self-hosted Playmatch instances by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3827 * feat: Support Range requests for multi-file ROM downloads by @&#8203;tmgast in https://github.com/rommapp/romm/pull/3167 * feat: add "Open in Flashpoint" game action by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3845 * feat: Surface never-scanned platform folders in the scan picker by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3847 * feat(platforms): add editable platform descriptions by @&#8203;Spinnich in https://github.com/rommapp/romm/pull/3855 * feat(v2): add random-ROM button to collections by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3860 * feat(v2): show smart and virtual collections on the home screen by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3859 * feat(scan): reassociate renamed/moved ROMs with missing entries by file hash by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3861 * feat(v2): auto-mark now-playing on launch + confirm launching shelved games by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3867 * feat: add music favorites and playlists API by @&#8203;tmgast in https://github.com/rommapp/romm/pull/3896 * feat: add `scan.priority.region_mode` to let config regions drive media selection by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3917 * feat: add Scan Settings to the user dropdown menu by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3923 * feat(tasks): allow orphaned resource cleanup to run on a schedule by @&#8203;XenuIsWatching in https://github.com/rommapp/romm/pull/3970 > \[!NOTE] > > ### Environment variables > > | variable | default | description | > | ------------------------------- | ----------------------------------------- | ---------------------------------------------------------------------------------- | > | PLAYMATCH\_API\_URL | `https://playmatch.retrorealm.dev/api/v2` | Use self-hosted instance of [Playmatch](https://github.com/RetroRealm/playmatch) | > | STREAMING\_BROKER\_SECRET | `-` | Required for emulator streaming | > | STREAMING\_SAVE\_TIMEOUT | `45` | Save request timeout (in seconds) | > | ROMM\_CORS\_ALLOWED\_ORIGINS | | Comma-separated list of allowed CORS origins (empty allows all) | > | ROMM\_SESSION\_SECURE\_COOKIE | `'false` | Mark session and CSRF cookies Secure (enable when served over HTTPS) | > | MAX\_ASSET\_UPLOAD\_SIZE\_BYTES | `536870912` | Max size of a save/state/screenshot upload request in bytes (0 disables the limit) | > | MAX\_AUTOCLEANUP\_LIMIT | `100` | Max number of saves a client can keep per slot when autocleanup is on (minimum 1) | #### Fixes - fix(scan): resolve "Reg-" prefixed region shortcodes to full names by [@&#8203;TowyTowy](https://github.com/TowyTowy) in [#&#8203;3754](https://github.com/rommapp/romm/pull/3754) - fix(playmatch): handle empty heartbeat query by [@&#8203;DevYukine](https://github.com/DevYukine) in [#&#8203;3756](https://github.com/rommapp/romm/pull/3756) - fix(db): pin explicit collation in virtual\_collections view for MariaDB 11.6+ by [@&#8203;kthistlewood](https://github.com/kthistlewood) in [#&#8203;3759](https://github.com/rommapp/romm/pull/3759) - fix(v2): render match-rom covers at full width in Safari by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3763](https://github.com/rommapp/romm/pull/3763) - fix(migrations): make DDL idempotent on partial-failure reruns by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3767](https://github.com/rommapp/romm/pull/3767) - fix(v2): link IGDB provider to game page by slug by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3782](https://github.com/rommapp/romm/pull/3782) - fix(v2): batch gallery cover fetches into windows by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3765](https://github.com/rommapp/romm/pull/3765) - fix(ss): tolerate ScreenScraper's invalid JSON escapes by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3778](https://github.com/rommapp/romm/pull/3778) - fix(backend): lightweight eager-load for /roms/{id}/simple by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3779](https://github.com/rommapp/romm/pull/3779) - fix(platforms): include TGDB handler in tgdb\_id fallback for supported platforms by [@&#8203;TowyTowy](https://github.com/TowyTowy) in [#&#8203;3785](https://github.com/rommapp/romm/pull/3785) - fix: respect configured IGDB region priority by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3791](https://github.com/rommapp/romm/pull/3791) - fix(retroachievements): resolve m3u playlists to natively hashed discs by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3804](https://github.com/rommapp/romm/pull/3804) - fix(retroachievements): extract non-zip archives before RAHasher hashing by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3813](https://github.com/rommapp/romm/pull/3813) - fix(metadata): search the whole filename for Sony serial codes by [@&#8203;TowyTowy](https://github.com/TowyTowy) in [#&#8203;3802](https://github.com/rommapp/romm/pull/3802) - fix(docker): proxy nginx to gunicorn over TCP instead of unix socket by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3810](https://github.com/rommapp/romm/pull/3810) - fix(frontend): base v2 verified badge on real DB hash matches by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3815](https://github.com/rommapp/romm/pull/3815) - fix(v2): render visible labels on device-auth and token expiry selects by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3818](https://github.com/rommapp/romm/pull/3818) - fix(igdb): don't match series games to their base title by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3817](https://github.com/rommapp/romm/pull/3817) - fix(scan): make v2 per-ROM "Update metadata" run the update scan type by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3819](https://github.com/rommapp/romm/pull/3819) - fix(v2): batch gallery hydration into windows and cap concurrency by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3824](https://github.com/rommapp/romm/pull/3824) - fix(v2): clear gallery selection after deleting ROMs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3828](https://github.com/rommapp/romm/pull/3828) - fix(player): keep origin save bound and surface save slots in v2 EmulatorJS by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3831](https://github.com/rommapp/romm/pull/3831) - fix: Missing last platform-stats progressbar by [@&#8203;AlmostEasyGoing](https://github.com/AlmostEasyGoing) in [#&#8203;3834](https://github.com/rommapp/romm/pull/3834) - fix(netplay): correct room-list and socket URLs behind a reverse proxy by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3839](https://github.com/rommapp/romm/pull/3839) - fix: don't re-stamp updated\_at for unchanged ROMs during scans by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3843](https://github.com/rommapp/romm/pull/3843) - fix: load selected file with correct extension for multi-file roms by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3844](https://github.com/rommapp/romm/pull/3844) - Fix v2 gallery not refetching when filter logic operator changes by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3840](https://github.com/rommapp/romm/pull/3840) - Fix wrong error wording for flag-only providers in setup wizard by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3841](https://github.com/rommapp/romm/pull/3841) - Fix slot-less save upload DB/file desync by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3846](https://github.com/rommapp/romm/pull/3846) - fix: enforce hidden-ROM/platform visibility on asset download and streaming endpoints by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3853](https://github.com/rommapp/romm/pull/3853) - fix(v2): ignore phantom/non-standard gamepads in nav polling by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3852](https://github.com/rommapp/romm/pull/3852) - fix(v2): overlay body-scroll-lock leaks + Library Stats label clipping by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3858](https://github.com/rommapp/romm/pull/3858) - fix(collections): lazy-load collection mosaic artwork by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3870](https://github.com/rommapp/romm/pull/3870) - fix(v2): keep collection visibility toggle in sync after save by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3871](https://github.com/rommapp/romm/pull/3871) - fix(db): don't use STR\_TO\_DATE in a generated column (MariaDB 12.3) by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3879](https://github.com/rommapp/romm/pull/3879) - fix(v2): take the hotkey-mute playing flag from EmulatorJS' start hook by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3878](https://github.com/rommapp/romm/pull/3878) - fix(archives): read RAR archives through bsdtar so they can be hashed by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3887](https://github.com/rommapp/romm/pull/3887) - fix(nginx): cache only fingerprinted assets, revalidate the rest by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3892](https://github.com/rommapp/romm/pull/3892) - fix(v2): hide empty platforms in Server Stats breakdown by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3899](https://github.com/rommapp/romm/pull/3899) - fix(scan): reconcile rom\_files in place instead of purging them by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3900](https://github.com/rommapp/romm/pull/3900) - fix(scan): emit a client update for ROMs that are no longer missing by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3903](https://github.com/rommapp/romm/pull/3903) - Fix/server stats duplicate slug key by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3906](https://github.com/rommapp/romm/pull/3906) - Fix Playmatch health check failing on plain-text response by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3916](https://github.com/rommapp/romm/pull/3916) - Stop storing fanart and title\_screen twice when screenshots are enabled by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3918](https://github.com/rommapp/romm/pull/3918) - fix(v2): restore backdrop-filter blur in production builds by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;3922](https://github.com/rommapp/romm/pull/3922) - Keep config.yaml EmulatorJS core settings active after user changes settings by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3920](https://github.com/rommapp/romm/pull/3920) - fix(migrations): pin device FK column collations to match devices.id by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3929](https://github.com/rommapp/romm/pull/3929) - fix(backend): make roms\_facets/collection migrations idempotent by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3930](https://github.com/rommapp/romm/pull/3930) - fix(roms): list the smart collections a ROM belongs to on its detail view by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3936](https://github.com/rommapp/romm/pull/3936) - fix(player): guard v2 EmulatorJS against stale saved disc id by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3940](https://github.com/rommapp/romm/pull/3940) - fix(player): render ScreenScraper bezel in v2 EmulatorJS player by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3944](https://github.com/rommapp/romm/pull/3944) - fix: default EmulatorJS netplay username to the RomM account by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3919](https://github.com/rommapp/romm/pull/3919) - fix: set page titles across v2 views by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3955](https://github.com/rommapp/romm/pull/3955) - fix: hide v2 actions the backend won't allow, with Playwright cover in CI by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3957](https://github.com/rommapp/romm/pull/3957) - fix: enforce hidden-rom visibility on rom child routes by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3960](https://github.com/rommapp/romm/pull/3960) - fix: always allow changing the virtual collection type in v2 settings by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3961](https://github.com/rommapp/romm/pull/3961) - fix: ROMM\_AUTH\_SECRET\_KEY auto-generation under nounset by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3967](https://github.com/rommapp/romm/pull/3967) - fix: serve each PKG file as its own FPKGi entry by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3966](https://github.com/rommapp/romm/pull/3966) - fix(resources): handle write failures when the disk is full by [@&#8203;XenuIsWatching](https://github.com/XenuIsWatching) in [#&#8203;3969](https://github.com/rommapp/romm/pull/3969) - fix(tasks): skip orphaned resource cleanup when the database looks empty by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3971](https://github.com/rommapp/romm/pull/3971) - fix(v2): use full document navigation for EmulatorJS player by [@&#8203;seffignoz](https://github.com/seffignoz) in [#&#8203;3973](https://github.com/rommapp/romm/pull/3973) - fix(scan): refuse concurrent scans and stop queued ones by [@&#8203;XenuIsWatching](https://github.com/XenuIsWatching) in [#&#8203;3974](https://github.com/rommapp/romm/pull/3974) - fix(frontend): only treat top-level game folder files as media assets by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3976](https://github.com/rommapp/romm/pull/3976) - fix(platforms): hide empty platforms behind a toolbar filter by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3982](https://github.com/rommapp/romm/pull/3982) - fix(docker): stop valkey rewriting its whole RDB every few minutes by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3983](https://github.com/rommapp/romm/pull/3983) - fix(gallery): pack placeholder covers at the active boxart ratio by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3987](https://github.com/rommapp/romm/pull/3987) - fix(launchbox): stop the cloud provider from silently matching nothing by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3986](https://github.com/rommapp/romm/pull/3986) #### Other changes - docs: add agent repository instructions by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3755](https://github.com/rommapp/romm/pull/3755) - Reduce RQ worker registry sweep verbosity and frequency by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3731](https://github.com/rommapp/romm/pull/3731) - chore(i18n): add linter enforcing alphabetically sorted locale files by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3757](https://github.com/rommapp/romm/pull/3757) - perf(backend): index roms (platform\_id, fs\_size\_bytes) by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3780](https://github.com/rommapp/romm/pull/3780) - perf(roms): make full rom id index opt-out on GET /api/roms by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3781](https://github.com/rommapp/romm/pull/3781) - revert(v2): hydrate gallery cards per-card via /roms/{id}/simple by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3789](https://github.com/rommapp/romm/pull/3789) - perf(backend): make roms\_metadata fast via generated columns by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3826](https://github.com/rommapp/romm/pull/3826) - feat(platforms): add windows9x support by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3880](https://github.com/rommapp/romm/pull/3880) - chore: add security-audit skill for vetting diffs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3872](https://github.com/rommapp/romm/pull/3872) - perf(backend): mirror ROM filter values into a narrow trigger-maintained table by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3885](https://github.com/rommapp/romm/pull/3885) - perf(backend): materialize virtual collection membership by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3886](https://github.com/rommapp/romm/pull/3886) - perf(stats): aggregate Server Stats breakdowns from roms\_facets by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3910](https://github.com/rommapp/romm/pull/3910) - perf(tasks): walk resource directories with os.scandir by [@&#8203;XenuIsWatching](https://github.com/XenuIsWatching) in [#&#8203;3972](https://github.com/rommapp/romm/pull/3972) - security: harden CORS, session cookies, filename sanitization, and upload limits by [@&#8203;Sunil56224972](https://github.com/Sunil56224972) in [#&#8203;3952](https://github.com/rommapp/romm/pull/3952) - build(deps): bump pillow from 12.2.0 to 12.3.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3863](https://github.com/rommapp/romm/pull/3863) - build(deps): bump pydantic-settings from 2.14.1 to 2.14.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3868](https://github.com/rommapp/romm/pull/3868) - build(deps-dev): bump fast-uri from 3.1.2 to 3.1.4 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3927](https://github.com/rommapp/romm/pull/3927) - build(deps): bump linkify-it from 5.0.1 to 5.0.2 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3926](https://github.com/rommapp/romm/pull/3926) - build(deps): bump postcss from 8.5.15 to 8.5.23 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3959](https://github.com/rommapp/romm/pull/3959) #### New Contributors - [@&#8203;kthistlewood](https://github.com/kthistlewood) made their first contribution in [#&#8203;3759](https://github.com/rommapp/romm/pull/3759) - [@&#8203;AlmostEasyGoing](https://github.com/AlmostEasyGoing) made their first contribution in [#&#8203;3834](https://github.com/rommapp/romm/pull/3834) - [@&#8203;SaveEditors](https://github.com/SaveEditors) made their first contribution in [#&#8203;3945](https://github.com/rommapp/romm/pull/3945) - [@&#8203;XenuIsWatching](https://github.com/XenuIsWatching) made their first contribution in [#&#8203;3970](https://github.com/rommapp/romm/pull/3970) - [@&#8203;seffignoz](https://github.com/seffignoz) made their first contribution in [#&#8203;3973](https://github.com/rommapp/romm/pull/3973) **Full Changelog**: <https://github.com/rommapp/romm/compare/5.0.0...5.1.0> ### [`v5.0.0`](https://github.com/rommapp/romm/releases/tag/5.0.0) [Compare Source](https://github.com/rommapp/romm/compare/4.9.2...5.0.0) <div align="center"> <img width="465" height="144" alt="romm-5 0-banner" src="https://github.com/user-attachments/assets/daba6e6c-5df3-4e5b-b7a1-c737ca265f0e" /> </div> #### Highlights ##### UI overhaul A ground-up redesign of the UI, with a new design system, fresh visual language, and a universal input model that feels at home with mouse, touch, keyboard and gamepad alike. With a stronger emphasis on performance, responsiveness, ease-of-use and accessibility, the new UI should be a joy on any device. [#&#8203;3436](https://github.com/rommapp/romm/issues/3436) <img width="1000" height="619" alt="Screenshot 2026-07-11 at 2 02 26 PM" src="https://github.com/user-attachments/assets/c1ad3c67-3ac5-48c6-a4e7-86b5b90a0e3d" /> <br /> <img width="1000" height="591" alt="Screenshot 2026-07-11 at 2 01 49 PM" src="https://github.com/user-attachments/assets/675bb1ea-8793-4b59-9a36-c3ac6b5de2f1" /> <br /> <img width="1000" height="677" alt="Screenshot 2026-07-11 at 3 06 19 PM" src="https://github.com/user-attachments/assets/017526a3-c70a-4bcd-9677-d838d286f833" /> ##### Shared savestates Share your game saves and savestates with other users; useful for speedrunning, special unlocks, new game +, or just showing off your progress. [#&#8203;3569](https://github.com/rommapp/romm/issues/3569) <img width="907" height="136" alt="Screenshot 2026-07-12 at 2 43 57 PM" src="https://github.com/user-attachments/assets/73596f33-3608-48c6-80ae-9e240b1219a1" /> ##### Interactive 3D boxart Boxart go weeeeeeeeeeeeeeeeeeeeeeeeeeeee! [#&#8203;3580](https://github.com/rommapp/romm/issues/3580) <img width="296" height="336" alt="Jul-11-2026 14-55-17" src="https://github.com/user-attachments/assets/85ea6e87-9387-409c-a3c5-f1101643be6e" /> ##### CRT mode Take me back to the 90s with a CRT shader that simulates the look of those old displays we love dearly. [#&#8203;3552](https://github.com/rommapp/romm/issues/3552) <img width="1000" height="529" alt="Screenshot 2026-07-12 at 2 47 24 PM" src="https://github.com/user-attachments/assets/d59dce11-9db4-4095-86e0-5d1a296756fa" /> ##### Server-side ROM patching We've unlocked the full power of ROM patching by moving it to the server. Simply apply patches to your ROMs, now with the option to use stored or uploaded patch files. [#&#8203;3225](https://github.com/rommapp/romm/issues/3225) <img width="1000" height="512" alt="Screenshot 2026-07-12 at 2 50 04 PM" src="https://github.com/user-attachments/assets/3370cd58-19db-46b8-8c17-838d1c2ac1eb" /> ##### QR code pairing Quickly pair your PC, TV or handheld device with your server by scanning a QR code (currently available on Argosy Launcher and Playnite). [#&#8203;3308](https://github.com/rommapp/romm/issues/3308) <img width="291" height="292" alt="Screenshot 2026-07-13 at 8 26 06 AM" src="https://github.com/user-attachments/assets/a6d1c186-05f3-4bb2-b2c0-de9d7a4fbade" /> ##### Real-time logs Tired of SSHing into your server to tail the logs when something breaks? Now you can view the backend logs in real-time directly from the web UI. [#&#8203;3557](https://github.com/rommapp/romm/issues/3557) <img width="1000" height="430" alt="Screenshot 2026-07-12 at 2 42 01 PM" src="https://github.com/user-attachments/assets/28354e7f-6256-4ee2-ace0-36557453fe81" /> ##### Controller debug view Test your controller inputs and mappings in real-time with a new debug view. [#&#8203;3581](https://github.com/rommapp/romm/issues/3581) <img width="577" height="420" alt="Screenshot 2026-07-13 at 8 23 47 AM" src="https://github.com/user-attachments/assets/2a48756e-2df0-4ed2-8fcd-565dbad8c67d" /> ##### Changelog preview A quick peak at the changes in the current version, with links to the full changelog. [#&#8203;3560](https://github.com/rommapp/romm/issues/3560) <img width="500" height="451" alt="Screenshot 2026-07-12 at 2 48 01 PM" src="https://github.com/user-attachments/assets/ab523b84-945d-4021-95c2-b88b92b24e3b" /> ##### Granular permission system Tweak access to your library with per-user and per-group permissions, with one configurable default group for new users. Tri-state grant/revoke on top of the group, and per-entity overrides, allow for fine-grained control over who can see what. [#&#8203;3605](https://github.com/rommapp/romm/issues/3605) <img width="548" height="628" alt="Screenshot 2026-07-13 at 8 22 43 AM" src="https://github.com/user-attachments/assets/2af6df22-e2b8-412c-b566-d332f8d62bdb" /> ##### Music player Music files stored within a ROMs' `soundtrack` folder can now be played directly from details page ([#&#8203;3610](https://github.com/rommapp/romm/issues/3610)), and queried directly from the API ([#&#8203;3640](https://github.com/rommapp/romm/issues/3640)). This opens the door to future features like background music playback and soundtrack playlists. <img width="906" height="664" alt="Screenshot 2026-07-13 at 6 56 42 AM" src="https://github.com/user-attachments/assets/2b6c5a02-a9ed-4fe4-b85e-43c29100a928" /> #### Minor changes * Add ES-DE `<sortname>` import and edit support for ROM ordering by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3460 * fix(ra): hash PSP compressed-ISO containers natively (#&#8203;3600) by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3609 * feat(roms): filter games by matched metadata provider by @&#8203;Spinnich in https://github.com/rommapp/romm/pull/3615 * feat(roms): filter games by custom tags by @&#8203;Spinnich in https://github.com/rommapp/romm/pull/3629 * Add ROM revision field to game details overview by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3631 * Add Artwork subtab to Media tab with scraped art gallery by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3634 and https://github.com/rommapp/romm/pull/3700 * Add filters for has save/state by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3642 * Support markdown manuals and enforce allowed file types by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3683 * feat(player): remember emulator core per-game by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3701 * feat: Add debug overlay by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/3554 #### Fixes * fix: Refresh platforms on scan by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/3555 * Handle missing ROM files during delete-from-filesystem requests by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/3562 * fix: guard SiblingBadge against missing sibling_roms (scan crash with Group ROMs) by @&#8203;androosio in https://github.com/rommapp/romm/pull/3568 * Permission fix for exported game lists and Pegasus platform slugs mapping by @&#8203;ametis70 in https://github.com/rommapp/romm/pull/3587 * fix(platform): accept embedded custom_name body on platform update by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3597 * fix(webp): backfill cover conversion on startup when enabled by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3602 * fix(scan): prevent duplicate ROM entries from racing scans by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3606 * fix(scan): make scan tracker totals reflect platforms actually scanned by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3607 * Fix state/save injection timing and corruption in EmulatorJS by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3624 * fix(scan): Normalize Sony serial codes to uppercase before cache lookup by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3622 * Fix ROM name handling in UNMATCHED scans to replace placeholders by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3623 * fix(backend): load platform_id on siblings to avoid DetachedInstanceError by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3635 * Fix ROM upload temp directory to use RESOURCES_BASE_PATH by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3632 * fix(roms): namespace filter-values cache by schema version by @&#8203;Spinnich in https://github.com/rommapp/romm/pull/3638 * fix(retroachievements): match MSX2 against RetroAchievements (#&#8203;3644) by @&#8203;Spinnich in https://github.com/rommapp/romm/pull/3645 * fix(retroachievements): match Famicom Disk System (FDS) against RetroAchievements by @&#8203;Spinnich in https://github.com/rommapp/romm/pull/3647 * fix(retroachievements): match SuperGrafx against RetroAchievements by @&#8203;Spinnich in https://github.com/rommapp/romm/pull/3652 * Fix typo in core mapping from 'zsx' to 'zxs' by @&#8203;sildur in https://github.com/rommapp/romm/pull/3656 * Refine filename version-tag parsing to avoid swallowing generic `v*` tags by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3658 * fix: make 1.8 migration re-entrant so fresh installs stop crash-looping on MariaDB by @&#8203;mvanhorn in https://github.com/rommapp/romm/pull/3669 * fix(retroachievements): hash GameCube and Wii RVZ/WIA images natively by @&#8203;Spinnich in https://github.com/rommapp/romm/pull/3657 * fix(collections): download whole collections as a single zip by @&#8203;Spinnich in https://github.com/rommapp/romm/pull/3661 * fix: display selected firmware in player by @&#8203;chiyi-creator in https://github.com/rommapp/romm/pull/3673 * fix: stop mapping Public Domain ROMs to Poland by @&#8203;chiyi-creator in https://github.com/rommapp/romm/pull/3671 * fix: allow NAT64-wrapped public IPs through SSRF filter by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3675 * Use parsed filename for unmatched ROM titles instead of raw filename by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3681 * fix(scan): fall back to other providers when ScreenScraper quota is exhausted by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3686 * fix(launchbox): import local media when matching via update_rom by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3687 * feat: default UI language to browser language when available by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3690 * fix(platforms): stop platform variants from renaming the parent platform by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3693 * fix: generate virtual collections from all metadata sources by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3694 * fix(sockets): require TASKS_RUN scope for scan and scan:stop by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3698 * fix(roms): enforce parent ROM visibility on direct file endpoints by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3703 * fix(backend): correct M4A embedded cover extraction and validation by @&#8203;VoidHandler in https://github.com/rommapp/romm/pull/3705 * fix(launchbox): match remote cover art region to ROM region by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3709 * fix(backend): serve PDF/Markdown manuals inline so the viewer can render them by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3714 * fix: surface config.yml parse errors instead of silently using defaults by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3713 * fix(frontend): restore manual display and extract ManualSubtab by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3717 * Fixed the translation of "player-count" by @&#8203;wolfg1969 in https://github.com/rommapp/romm/pull/3719 * fix(scan): make Hasheous and SteamGridDB scan lookups fault-tolerant in the metadata handlers by @&#8203;TowyTowy in https://github.com/rommapp/romm/pull/3716 * fix(backend): discard ScreenScraper chroma-key green placeholder art by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3724 * fix(frontend): restore manual display and extract ManualSubtab by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3717 * Fixed the translation of "player-count" by @&#8203;wolfg1969 in https://github.com/rommapp/romm/pull/3719 * fix(scan): make Hasheous and SteamGridDB scan lookups fault-tolerant in the metadata handlers by @&#8203;TowyTowy in https://github.com/rommapp/romm/pull/3716 * fix(backend): discard ScreenScraper chroma-key green placeholder art by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3724 * fix(ui): remove cover-art attribution link on details pages by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3737 fix(scan): strip leading dot from dot-separated version tags by @&#8203;TowyTowy in https://github.com/rommapp/romm/pull/3751 #### Other changes * fix(search): full-text indexes+caching by @&#8203;bonofiglio in https://github.com/rommapp/romm/pull/3540 * Integrate Hypothesis property-based testing for the backend by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3403 * feat: backend connection status notice + offline-aware boot by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/3550 * Run backend tests in parallel with pytest-xdist by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3559 * feat(auth): add OIDC_ALLOW_REGISTRATION toggle to gate OIDC auto-registration by @&#8203;Spinnich in https://github.com/rommapp/romm/pull/3577 * docs: split frontend constitution into focused agent skills, make CLAUDE.md holistic by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3581 * Exclude served resources from Vite dev watcher by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3586 * Add test data generator script by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3585 * Test Build GitHub Action improvements by @&#8203;ametis70 in https://github.com/rommapp/romm/pull/3588 * fix(deps): bump dependencies to patch reported vulnerabilities by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3592 * Turkish language support tr_TR by @&#8203;y1lm-z in https://github.com/rommapp/romm/pull/3593 * perf(roms): speed up the gallery/search list endpoint on large libraries by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3595 * Stop spamming PRs with coverage reports by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3625 * Squash legacy migrations by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3670 * docs(claude): add PR linking and template conventions by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3682 * i18n: rename "Duplicates" filter to "Versions" in the UI by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3688 * test: add axe accessibility gate + keyboard nav tests to Storybook by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3691 * docs(sync): clarify slot pairing behavior in negotiate OpenAPI spec by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3699 * feat(playmatch): move integration to API v2 by @&#8203;DevYukine in https://github.com/rommapp/romm/pull/3689 * ci: fail PRs that modify the frozen frontend v1 UI by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3723 * Hotfix upload buttons in media tabs by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3732 * ci: fail PRs that modify the frozen frontend v1 UI by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3723 * Add Freegosy to community tab in README by @&#8203;abduznik in https://github.com/rommapp/romm/pull/3729 * Update README featured projects by @&#8203;QuaiGoner in https://github.com/rommapp/romm/pull/3738 * feat: inject ScreenScraper dev credentials at build time by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3740 * build(deps): bump python-engineio from 4.12.2 to 4.13.2 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3626 * build(deps): bump joserfc from 1.6.3 to 1.6.7 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3627 * build(deps): bump python-socketio from 5.14.0 to 5.16.2 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3628 * chore(deps): bump joserfc from 1.6.7 to 1.6.8 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3672 #### New Contributors * @&#8203;bonofiglio made their first contribution in https://github.com/rommapp/romm/pull/3540 * @&#8203;androosio made their first contribution in https://github.com/rommapp/romm/pull/3568 * @&#8203;ametis70 made their first contribution in https://github.com/rommapp/romm/pull/3587 * @&#8203;y1lm-z made their first contribution in https://github.com/rommapp/romm/pull/3593 * @&#8203;sildur made their first contribution in https://github.com/rommapp/romm/pull/3656 * @&#8203;mvanhorn made their first contribution in https://github.com/rommapp/romm/pull/3669 * @&#8203;chiyi-creator made their first contribution in https://github.com/rommapp/romm/pull/3673 * @&#8203;VoidHandler made their first contribution in https://github.com/rommapp/romm/pull/3705 * @&#8203;wolfg1969 made their first contribution in https://github.com/rommapp/romm/pull/3719 * @&#8203;TowyTowy made their first contribution in https://github.com/rommapp/romm/pull/3716 **Full Changelog**: <https://github.com/rommapp/romm/compare/4.9.2...5.0.0> ### [`v4.9.2`](https://github.com/rommapp/romm/releases/tag/4.9.2) [Compare Source](https://github.com/rommapp/romm/compare/4.9.1...4.9.2) #### What's Changed - fix: Crash on startup when no library by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;3544](https://github.com/rommapp/romm/pull/3544) - build(deps-dev): bump js-yaml from 4.1.1 to 4.2.0 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3525](https://github.com/rommapp/romm/pull/3525) - build(deps): bump tornado from 6.5.6 to 6.5.7 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3528](https://github.com/rommapp/romm/pull/3528) - build(deps): bump python-multipart from 0.0.22 to 0.0.31 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3527](https://github.com/rommapp/romm/pull/3527) - build(deps): bump aiohttp from 3.13.4 to 3.14.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3530](https://github.com/rommapp/romm/pull/3530) - build(deps): bump markdown-it from 14.1.1 to 14.2.0 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3533](https://github.com/rommapp/romm/pull/3533) - build(deps): bump ws and engine.io-client in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3532](https://github.com/rommapp/romm/pull/3532) **Full Changelog**: <https://github.com/rommapp/romm/compare/4.9.1...4.9.2> ### [`v4.9.1`](https://github.com/rommapp/romm/releases/tag/4.9.1) [Compare Source](https://github.com/rommapp/romm/compare/4.9.0...4.9.1) #### What's Changed - fix: Structure A always takes priority over Structure B in library detection by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3508](https://github.com/rommapp/romm/pull/3508) - build(deps): bump tornado from 6.5.5 to 6.5.6 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3513](https://github.com/rommapp/romm/pull/3513) - build(deps): bump esbuild, vite, [@&#8203;tailwindcss/vite](https://github.com/tailwindcss/vite), [@&#8203;vitejs/plugin-vue](https://github.com/vitejs/plugin-vue), vite-plugin-pwa and vite-plugin-static-copy in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3514](https://github.com/rommapp/romm/pull/3514) - Fix config.yml serialization breaking folder-to-platform mappings by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3516](https://github.com/rommapp/romm/pull/3516) - Override rolldown version by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3524](https://github.com/rommapp/romm/pull/3524) - Fix bypass\_autologin overwritten by 403 interceptor during app initialization by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3523](https://github.com/rommapp/romm/pull/3523) **Full Changelog**: <https://github.com/rommapp/romm/compare/4.9.0...4.9.1> ### [`v4.9.0`](https://github.com/rommapp/romm/releases/tag/4.9.0) [Compare Source](https://github.com/rommapp/romm/compare/4.8.1...4.9.0) > \[!WARNING] > The structure of scan exports in `config.yml` has changed! If you're currently exporting `gamelist.xml` files with `scan.export_gamelist`, migrate to `scan.export.gamelist_xml: true` #### Highlights ##### Save sync engine "Coming soon" no more! Third-party app developers will now be able to rely on RomM to keep save files in sync across devices and platforms. [#&#8203;3137](https://github.com/rommapp/romm/issues/3137) Devices (apps, handhelds, browsers) can choose to register themselves with the server, which uniquely identifies them with a token. The system tracks which device has the latest version of a save file, flags conflicts when two devices change the same save, and syncs in whichever way fits the device: on demand, via a watched folder, or through a scheduled background sync. ##### Play session tracking Another one for the app developers: client devices submit batches of play sessions per device, recording when and how long games were played. Sessions track both the time window (start\_time/end\_time) and screen-on time (duration\_ms), which may differ due to device suspension or back-grounding. [#&#8203;3155](https://github.com/rommapp/romm/issues/3155) ##### Libretro thumbnails The [libretro thumbnail repository](https://thumbnails.libretro.com/) is now available as a first-class artwork source for region-correct box art (PAL/Europe, Japan, etc.). Matching is exact case-insensitive against the directory listing, so a ROM named "(Europe)" lands on the (Europe) artwork. The source is enabled by default, and you can set it's priority in your `config.yml` via the `scan.priority.artwork` list. [#&#8203;3252](https://github.com/rommapp/romm/issues/3252) ##### Pegasus metadata export You can now auto-export a [Pegasus](https://pegasus-frontend.org/) compatible `metadata.pegasus.txt` file, just like the existing `gamelist.xml` export. Enable it in your `config.yml` file by setting `scan.export.pegasus` to true, and it should appear in each platform folder (alongside your ROMs). [#&#8203;3153](https://github.com/rommapp/romm/issues/3153) #### Notable changes - Gamelist export improvements by [@&#8203;vargash](https://github.com/vargash) in [#&#8203;3202](https://github.com/rommapp/romm/pull/3202) - Send a suggestion to playmatch on manual match when enabled by [@&#8203;DevYukine](https://github.com/DevYukine) in [#&#8203;3296](https://github.com/rommapp/romm/pull/3296) - Add SteamGridDB, ScreenScraper, MobyGames & Launchbox hash support to Playmatch by [@&#8203;DevYukine](https://github.com/DevYukine) in [#&#8203;3381](https://github.com/rommapp/romm/pull/3381) - Compute raw CHD hashes and route disc-data SHA1 to Hasheous by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3385](https://github.com/rommapp/romm/pull/3385) - Add Playmatch as explicit metadata source by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3407](https://github.com/rommapp/romm/pull/3407) - Add genesis\_plus\_gx\_wide, bsnes, freeintv, azahar EJS cores to nightly by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3454](https://github.com/rommapp/romm/pull/3454) - Expose per-device save sync attribution and origin device by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;3479](https://github.com/rommapp/romm/issues/3479) - Add rate limiting to metadata service API requests by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3482](https://github.com/rommapp/romm/issues/3482) and [#&#8203;3485](https://github.com/rommapp/romm/issues/3485) #### Fixes - 4.8.1 hotfixes by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3221](https://github.com/rommapp/romm/pull/3221) - UI settings not woking after switching pages back and forth by [@&#8203;Namaneo](https://github.com/Namaneo) in [#&#8203;3317](https://github.com/rommapp/romm/pull/3317) - MSDOS games fail to launch in console mode ("Error for site owner / Check console") by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3294](https://github.com/rommapp/romm/pull/3294) - Include `cus` region in default ScreenScraper fallback regions by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3273](https://github.com/rommapp/romm/pull/3273) - Scan stalls on platforms with 10k+ already-scanned ROMs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3249](https://github.com/rommapp/romm/pull/3249) - Respect LOGLEVEL env var across all log output sources by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3243](https://github.com/rommapp/romm/pull/3243) - Registration links redirect to login for unauthenticated users by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3231](https://github.com/rommapp/romm/pull/3231) - Address bugs and add test coverage for save-sync by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;3135](https://github.com/rommapp/romm/pull/3135) - Fix age\_ratings Pydantic validation error on MySQL/MariaDB for single-element arrays by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3223](https://github.com/rommapp/romm/pull/3223) - Parse Content-Type essence before validating resource downloads by [@&#8203;TechnicallyComputers](https://github.com/TechnicallyComputers) in [#&#8203;3233](https://github.com/rommapp/romm/pull/3233) - Fix content\_hash not updated by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3234](https://github.com/rommapp/romm/pull/3234) - Fix extension exclusion not matching multi-dot filenames by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3326](https://github.com/rommapp/romm/pull/3326) - Fix chunked uploads writing to RAM/tmpfs and upload speed showing 0 B/s by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3316](https://github.com/rommapp/romm/pull/3316) - Fix RuffleRS scaling and overflow issues on large and small screens by [@&#8203;jondycz](https://github.com/jondycz) in [#&#8203;3245](https://github.com/rommapp/romm/pull/3245) - Fix RAHasher prod build by [@&#8203;dangmai](https://github.com/dangmai) in [#&#8203;3282](https://github.com/rommapp/romm/pull/3282) - Fix download failing when hash has not been computed by [@&#8203;Namaneo](https://github.com/Namaneo) in [#&#8203;3264](https://github.com/rommapp/romm/pull/3264) - Fix race condition in collection and favorite rom membership updates by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3263](https://github.com/rommapp/romm/pull/3263) - Fix settings drawer logout button overflowing on mobile by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3248](https://github.com/rommapp/romm/pull/3248) - Improve light mode text by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3236](https://github.com/rommapp/romm/pull/3236) - Resolve MariaDB errors in migration downgrades (0068, 0072) by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3246](https://github.com/rommapp/romm/pull/3246) - Show TheGamesDB link on platform drawer when tgdb\_id is set by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3254](https://github.com/rommapp/romm/pull/3254) - A bunch of minor bug fixes by [@&#8203;Namaneo](https://github.com/Namaneo) in [#&#8203;3270](https://github.com/rommapp/romm/pull/3270) - Re-parse tags from filename when renaming a rom by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;3295](https://github.com/rommapp/romm/pull/3295) - Open refresh metadata dialog with scan type and metadata source selection by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3293](https://github.com/rommapp/romm/pull/3293) - Setup wizard: make platform row click toggle selection checkbox by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3341](https://github.com/rommapp/romm/pull/3341) - Use static auth background on Firefox by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3352](https://github.com/rommapp/romm/pull/3352) - Validate uploaded images with libmagic by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3353](https://github.com/rommapp/romm/pull/3353) - Tolerate forward-compat values and malformed YAML in config loader by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3364](https://github.com/rommapp/romm/pull/3364) - Apply `DISABLE_EMULATOR_JS` to Console mode (disable Play + block direct player route) by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3373](https://github.com/rommapp/romm/pull/3373) - Fix stale disc file ID in localStorage causing 404 on ROM play after rescan by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3374](https://github.com/rommapp/romm/pull/3374) - Fix `gamelist.xml` export to use relative media paths for local exports by [@&#8203;gtronset](https://github.com/gtronset) in [#&#8203;3369](https://github.com/rommapp/romm/pull/3369) - Include games without achievements in RA hash cache by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3368](https://github.com/rommapp/romm/pull/3368) - Generate gunicorn logging config to writable /tmp path by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3383](https://github.com/rommapp/romm/pull/3383) - Avoid Safari/WebKit stalls during chunked ROM uploads by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3382](https://github.com/rommapp/romm/pull/3382) - Default SYNC\_BASE\_PATH to /var/lib/romm/sync by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3386](https://github.com/rommapp/romm/pull/3386) - Exclude the videos directory from scanning for batocera config by [@&#8203;enlewof](https://github.com/enlewof) in [#&#8203;3387](https://github.com/rommapp/romm/pull/3387) - Treat Libretro-only ROM matches as identified during scan by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3394](https://github.com/rommapp/romm/pull/3394) - Fix Complete Rescan not clearing unselected metadata sources by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3395](https://github.com/rommapp/romm/pull/3395) - Respect region priority for multi-region ROMs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3396](https://github.com/rommapp/romm/pull/3396) - Tolerate launchbox basepath by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3397](https://github.com/rommapp/romm/pull/3397) - Harden multi-ROM folder exclusion matching for ES-DE media directories by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3399](https://github.com/rommapp/romm/pull/3399) - Fix: UNMATCHED scan ignores manually-set metadata IDs when metadata is absent by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3401](https://github.com/rommapp/romm/pull/3401) - Fix IGDB/SGDB matching wrong game for standalone expansions (e.g. Ecco: The Tides of Time) by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3400](https://github.com/rommapp/romm/pull/3400) - fix: wipe downloaded resources on COMPLETE rescan by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3406](https://github.com/rommapp/romm/pull/3406) - fix(screenscraper): re-add user credentials to media file downloads by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3414](https://github.com/rommapp/romm/pull/3414) - fix(screenscraper): skip name search after notgame hash lookup by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3417](https://github.com/rommapp/romm/pull/3417) - Handle malformed ES-DE `<alternativeEmulator>` tags during gamelist.xml import by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3421](https://github.com/rommapp/romm/pull/3421) - Support `<folder>` entries in gamelist.xml metadata import by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3422](https://github.com/rommapp/romm/pull/3422) - fix(nginx): use Buffer for binary-safe base64 decode in m3u endpoint by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3428](https://github.com/rommapp/romm/pull/3428) - Add iOS pseudo-fullscreen shim for EmulatorJS player by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3431](https://github.com/rommapp/romm/pull/3431) - perf(roms): avoid hydrating full Rom rows for siblings on list endpoint by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3432](https://github.com/rommapp/romm/pull/3432) - fix(screenscraper): inject user credentials for all standard media downloads by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3438](https://github.com/rommapp/romm/pull/3438) - Use region-prioritized release dates from ScreenScraper by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3434](https://github.com/rommapp/romm/pull/3434) - chore: remove obsolete 'version' attribute by [@&#8203;VewDev](https://github.com/VewDev) in [#&#8203;3443](https://github.com/rommapp/romm/pull/3443) - feat: add `miximage_v2` media type to explicitly select SS.fr mixrbv2 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3446](https://github.com/rommapp/romm/pull/3446) - Fix HTTP 500 on ROM list when platform has ROMs with NULL names by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3450](https://github.com/rommapp/romm/pull/3450) - Fix save-sync hash drift, archival save leak, and dedupe scoping by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;3448](https://github.com/rommapp/romm/pull/3448) - Composite hashing for compressed archives by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3412](https://github.com/rommapp/romm/pull/3412) - fix(screenscraper): use internal filename as romnom for single-file archive games by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3451](https://github.com/rommapp/romm/pull/3451) - Fix save-sync negotiate thrash and unblock content\_hash recompute by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;3453](https://github.com/rommapp/romm/pull/3453) - fix(hashing): compute RA hash for archive ROMs on cartridge platforms by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3455](https://github.com/rommapp/romm/pull/3455) - fix(igdb): match ROMs by localized/alternative titles in scan by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3461](https://github.com/rommapp/romm/pull/3461) - Match IGDB regional-twin platforms in scans ([#&#8203;3462](https://github.com/rommapp/romm/issues/3462)) by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3463](https://github.com/rommapp/romm/pull/3463) - fix(roms): repair multi-file ROM downloads broken by deferred file stats by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3466](https://github.com/rommapp/romm/pull/3466) - fix(ss): map New Nintendo 3DS to ScreenScraper system 17 by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3465](https://github.com/rommapp/romm/pull/3465) - Fix double URL-encoding of ScreenScraper name-search term by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3468](https://github.com/rommapp/romm/pull/3468) - test(roms): cover region-tag re-parse on rename ([#&#8203;3471](https://github.com/rommapp/romm/issues/3471)) by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3472](https://github.com/rommapp/romm/pull/3472) - Invalidate EmulatorJS ROM cache when a ROM filename changes by [@&#8203;Copilot](https://github.com/Copilot) in [#&#8203;3476](https://github.com/rommapp/romm/pull/3476) - fix(roms): return 404 when content file\_ids match no files by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3473](https://github.com/rommapp/romm/pull/3473) - fix(screenscraper): utilize ss.fr jeuinfos.php endpoint for non-hashable platforms by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3475](https://github.com/rommapp/romm/pull/3475) - Handle missing console bezel assets without rendering Safari broken-image overlay by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3494](https://github.com/rommapp/romm/issues/3494) - Fix smart collection status filter normalization for multi-value legacy payloads by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3495](https://github.com/rommapp/romm/issues/3495) - Send all top-level file hashes to Hasheous lookup by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3498](https://github.com/rommapp/romm/issues/3498) - fix(ra): hash folder-based disc ROMs (.cue + .bin) for RetroAchievements by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3500](https://github.com/rommapp/romm/issues/3500) #### Other changes - Safer migrations by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3238](https://github.com/rommapp/romm/pull/3238) - Skip RAHasher subprocess for archived disc-platform ROMs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3253](https://github.com/rommapp/romm/pull/3253) - Add missing index on `rom_files(rom_id)` to fix slow game listing on large ROM sets by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3259](https://github.com/rommapp/romm/pull/3259) - Enforce LF line endings for shell scripts via .gitattributes by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3266](https://github.com/rommapp/romm/pull/3266) - Describe architecture by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;3299](https://github.com/rommapp/romm/pull/3299) - Replace HIGH\_PRIO\_STRUCTURE\_PATH by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3322](https://github.com/rommapp/romm/pull/3322) - Replace deprecated `job.get_id()` with `job.id` for rq >= 2.7.0 compatibility by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3320](https://github.com/rommapp/romm/pull/3320) - Update config.batocera-retrobat.yml by [@&#8203;enlewof](https://github.com/enlewof) in [#&#8203;3349](https://github.com/rommapp/romm/pull/3349) - Prefer ROM's own region tag for ScreenScraper and IGDB artwork by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3367](https://github.com/rommapp/romm/pull/3367) - Add comma-separated metadata attribute support to gamelist by [@&#8203;EntropyEngineer](https://github.com/EntropyEngineer) in [#&#8203;3380](https://github.com/rommapp/romm/pull/3380) - Improve API handling, KO scrape data, and metadata sanitization by [@&#8203;Spinnich](https://github.com/Spinnich) in [#&#8203;3384](https://github.com/rommapp/romm/pull/3384) - Hardlink import/export assets, harden sync init by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3388](https://github.com/rommapp/romm/pull/3388) - Guard init startup against OpenTelemetry boot-loop conditions by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3423](https://github.com/rommapp/romm/pull/3423) - Refresh CSRF cookie on OIDC session authentication changes by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3424](https://github.com/rommapp/romm/pull/3424) - Defer optional handler initialization with lazy factories by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3426](https://github.com/rommapp/romm/pull/3426) - Denormalize ROM file stats for efficient gallery rendering by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3425](https://github.com/rommapp/romm/pull/3425) - Add DNS resolution check to URL validation by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3441](https://github.com/rommapp/romm/pull/3441) - Add tgcd and supergrafx to emulatorjs platform list by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3459](https://github.com/rommapp/romm/pull/3459) - Stop serving private user assets via unauthenticated nginx static route by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3480](https://github.com/rommapp/romm/issues/3480) - Fix Docker volume mounts to enable hardlink optimization by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3484](https://github.com/rommapp/romm/issues/3484) - Add opt-in files/siblings expansion to GET /api/roms by [@&#8203;tmgast](https://github.com/tmgast) in [#&#8203;3490](https://github.com/rommapp/romm/issues/3490) - Bump RAHasher to 1.8.3 in docker/Dockerfile by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3277](https://github.com/rommapp/romm/pull/3277) - Bump pytest-asyncio for pytest 9 compatibility by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3269](https://github.com/rommapp/romm/pull/3269) - Bump vite from 6.4.1 to 6.4.2 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3227](https://github.com/rommapp/romm/pull/3227) - Bump postcss from 8.5.8 to 8.5.12 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3321](https://github.com/rommapp/romm/pull/3321) - Bump axios from 1.13.6 to 1.15.0 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3240](https://github.com/rommapp/romm/pull/3240) - Bump python-dotenv from 1.0.1 to 1.2.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3301](https://github.com/rommapp/romm/pull/3301) - Bump follow-redirects from 1.15.11 to 1.16.0 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3267](https://github.com/rommapp/romm/pull/3267) - Bump pillow from 12.1.1 to 12.2.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3257](https://github.com/rommapp/romm/pull/3257) - Bump mako from 1.3.10 to 1.3.11 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3304](https://github.com/rommapp/romm/pull/3304) - Bump pytest from 8.4.1 to 9.0.3 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3268](https://github.com/rommapp/romm/pull/3268) - Bump fast-uri from 3.1.0 to 3.1.2 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3345](https://github.com/rommapp/romm/pull/3345) - Bump serialize-javascript and workbox-build in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3346](https://github.com/rommapp/romm/pull/3346) - Bump cryptography from 46.0.5 to 46.0.7 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3351](https://github.com/rommapp/romm/pull/3351) - Bump [@&#8203;babel/plugin-transform-modules-systemjs](https://github.com/babel/plugin-transform-modules-systemjs) from 7.29.0 to 7.29.4 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3350](https://github.com/rommapp/romm/pull/3350) - Bump authlib from 1.6.9 to 1.6.12 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3370](https://github.com/rommapp/romm/pull/3370) - Bump idna from 3.10 to 3.15 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3391](https://github.com/rommapp/romm/pull/3391) - Bump js-cookie from 3.0.5 to 3.0.7 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3404](https://github.com/rommapp/romm/pull/3404) - Bump asyncssh from 2.22.0 to 2.23.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3442](https://github.com/rommapp/romm/pull/3442) - Upgrade FastAPI to 0.134.0 and Starlette to 1.0.1 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3437](https://github.com/rommapp/romm/pull/3437) #### New Contributors - [@&#8203;TechnicallyComputers](https://github.com/TechnicallyComputers) made their first contribution in [#&#8203;3233](https://github.com/rommapp/romm/pull/3233) - [@&#8203;jondycz](https://github.com/jondycz) made their first contribution in [#&#8203;3245](https://github.com/rommapp/romm/pull/3245) - [@&#8203;Spinnich](https://github.com/Spinnich) made their first contribution in [#&#8203;3263](https://github.com/rommapp/romm/pull/3263) - [@&#8203;Namaneo](https://github.com/Namaneo) made their first contribution in [#&#8203;3264](https://github.com/rommapp/romm/pull/3264) - [@&#8203;dangmai](https://github.com/dangmai) made their first contribution in [#&#8203;3282](https://github.com/rommapp/romm/pull/3282) - [@&#8203;enlewof](https://github.com/enlewof) made their first contribution in [#&#8203;3349](https://github.com/rommapp/romm/pull/3349) - [@&#8203;EntropyEngineer](https://github.com/EntropyEngineer) made their first contribution in [#&#8203;3380](https://github.com/rommapp/romm/pull/3380) - [@&#8203;gtronset](https://github.com/gtronset) made their first contribution in [#&#8203;3369](https://github.com/rommapp/romm/pull/3369) - [@&#8203;VewDev](https://github.com/VewDev) made their first contribution in [#&#8203;3443](https://github.com/rommapp/romm/pull/3443) **Full Changelog**: <https://github.com/rommapp/romm/compare/4.8.1...4.9.0-beta.1> ### [`v4.8.1`](https://github.com/rommapp/romm/releases/tag/4.8.1) [Compare Source](https://github.com/rommapp/romm/compare/4.8.0...4.8.1) #### What's Changed - make loading per-platform stats opt-in so homepage doesn't fetch them by [@&#8203;cciollaro](https://github.com/cciollaro) in [#&#8203;3158](https://github.com/rommapp/romm/pull/3158) - fix: correct ZX Spectrum +3 ROM hashes in known\_bios\_files.json by [@&#8203;Abdess](https://github.com/Abdess) in [#&#8203;3191](https://github.com/rommapp/romm/pull/3191) - Return link for romm-mobile app to README.md by [@&#8203;milktoastrat](https://github.com/milktoastrat) in [#&#8203;3207](https://github.com/rommapp/romm/pull/3207) - Add outbound HTTP proxy support for backend requests by [@&#8203;dafal](https://github.com/dafal) in [#&#8203;3189](https://github.com/rommapp/romm/pull/3189) - chore(deps): bump lodash from 4.17.23 to 4.18.1 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3212](https://github.com/rommapp/romm/pull/3212) - 4.8 hotfixes by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3213](https://github.com/rommapp/romm/pull/3213) - Fix 500 on `/api/tasks/status` after `cleanup_missing_roms` task completes by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3214](https://github.com/rommapp/romm/pull/3214) - Fix registration invite links losing token on external redirect by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3215](https://github.com/rommapp/romm/pull/3215) - fix: correctly delete nested (subdirectory) ROMs from filesystem by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3216](https://github.com/rommapp/romm/pull/3216) - Fix GET /api/tasks/status 500 on expired job TTL (NoSuchJobError) by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3217](https://github.com/rommapp/romm/pull/3217) #### New Contributors - [@&#8203;Abdess](https://github.com/Abdess) made their first contribution in [#&#8203;3191](https://github.com/rommapp/romm/pull/3191) - [@&#8203;dafal](https://github.com/dafal) made their first contribution in [#&#8203;3189](https://github.com/rommapp/romm/pull/3189) **Full Changelog**: <https://github.com/rommapp/romm/compare/4.8.0...4.8.1> ### [`v4.8.0`](https://github.com/rommapp/romm/releases/tag/4.8.0) [Compare Source](https://github.com/rommapp/romm/compare/4.7.0...4.8.0) #### Highlights ##### Client API tokens API tokens are a safer way for apps and devices to connect without sharing user credentials. A new QR-based pairing flow makes setup quick, with support for regeneration and revocation when needed. [#&#8203;3114](https://github.com/rommapp/romm/issues/3114) | <img width="1049" height="683" alt="Screenshot 2026-03-13 at 9 15 48 PM" src="https://github.com/user-attachments/assets/809264d6-04b0-4ecb-8d12-0f72edc9d8c8" /> | <img width="577" height="451" alt="Screenshot 2026-03-13 at 9 25 26 PM" src="https://github.com/user-attachments/assets/8477ab18-79cf-448c-a28e-6c18508319e3" /> | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | ##### Chunked uploads Goodbye failed uploads and incessant timeouts! ROMs will now upload in smaller chunks, allowing for more reliable transfers even on spotty connections. [#&#8203;3076](https://github.com/rommapp/romm/issues/3076) <img width="363" height="118" alt="Screenshot 2026-03-13 at 9 17 24 PM" src="https://github.com/user-attachments/assets/be622a83-cfcd-4368-8ab9-7cdbb25c5ca5" /> ##### Local LaunchBox metadata import Have a local LaunchBox installation packed with games? You can now import metadata directly from it, including cover art, screenshots and video previews. [#&#8203;2922](https://github.com/rommapp/romm/issues/2922) <img width="822" height="374" alt="Screenshot 2026-03-13 at 9 26 29 PM" src="https://github.com/user-attachments/assets/738c22af-9502-4a42-b7e0-d5b676bd0bfb" /> #### Minor changes * Add config option to use local video paths in gamelist.xml export by @&#8203;JamieeBond in https://github.com/rommapp/romm/pull/3082 * Add additional cover styles for boxart to prevent cropping by @&#8203;Drarox in https://github.com/rommapp/romm/pull/3060 * Fetch games on search view mount and reset search state on input clear by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/3080 * Add `video_normalized` media type option for ScreenScraper scans by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3088 * Scrape and store age rating data from ScreenScraper.fr by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3089 * Make invite token expiration configurable by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3094 * Click to zoom game cover image by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3093 * Add more platforms to cover styles by @&#8203;Drarox in https://github.com/rommapp/romm/pull/3098 * Show filtered ROM count in the gallery AppBar and filter drawer by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3097 * Show firmware count in scan results to improve BIOS detection discoverability by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3111 * Per-platform metadata coverage and region breakdown to server stats by @&#8203;cciollaro in https://github.com/rommapp/romm/pull/3110 * Use PICO-8 built-in cover art from .p8.png cartridge files by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3119 * Auto-update game status from RetroAchievements award kind during progress sync by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3118 * Add Bulgarian (bg_BG) translation by @&#8203;DenislavDenev in https://github.com/rommapp/romm/pull/3159 #### Fixes * Fix platform aspect ratio not applying in gallery view by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3083 * Fix metadata IDs not persisting when handler is disabled or game not found by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3085 * Hack for natural sorting of ROMs by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3086 * Always sub dash with colon for launchbox matching by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3090 * Fix 500 error when loading platforms with ROMs whose filenames start with region tags by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3091 * Fix M3U generation to exclude raw .bin tracks when .cue files are present by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3100 * Filesystem watcher and scheduled scan skip when no metadata sources configured by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3106 * Replace deep watch with computed to reduce reactivity overhead by @&#8203;cciollaro in https://github.com/rommapp/romm/pull/3104 * Add missing platforms to playable game filter by @&#8203;Bergbok in https://github.com/rommapp/romm/pull/3121 * Default to querying all roms by @&#8203;camhorn in https://github.com/rommapp/romm/pull/3122 * Remove cover image card bevel by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3131 * Auto-select firmware when only one option is available by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3150 * Fix IntegrityError when updating favourites after ROM rescan by @&#8203;Copilot in https://github.com/rommapp/romm/pull/3166 * Fix mod_zip UTF-8 filename handling for non-ASCII downloads by @&#8203;tmgast in https://github.com/rommapp/romm/pull/3171 #### Other changes * Typed API form fields by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3036 * Fix OAuth token expiry and implement refresh token rotation by @&#8203;HydroSulphide in https://github.com/rommapp/romm/pull/3102 * Add property for path_video and refactor places which use similar logic by @&#8203;JamieeBond in https://github.com/rommapp/romm/pull/3116 * Move "Clean up All" missing ROMs deletion to background task by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3109 * Allow partial updates to ROM user props via typed payload schema by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3124 * Switch ACCESS_TOKEN_EXPIRE_MINUTES to SECONDS by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3096 * Update RAHasher to version 1.8.3 and add Wii platform support by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/3145 * Add OIDC_SERVER_METADATA_URL to support providers with non-standard discovery URLs (e.g. Google) by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3099 * Fix OIDC login role downgrading for users without claims by @&#8203;pacnpal in https://github.com/rommapp/romm/pull/3103 * Implement OIDC RP-Initiated Logout (end-session endpoint) by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3107 * Make OAuth token expiry configurable via env vars by @&#8203;HydroSulphide in https://github.com/rommapp/romm/pull/3126 * Remove Non-Argosy Android app references from README by @&#8203;milktoastrat in https://github.com/rommapp/romm/pull/3130 * Lockdown package installs by @&#8203;gantoine in https://github.com/rommapp/romm/pull/3203 * Bump authlib from 1.6.6 to 1.6.7 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3070 * Bump tar from 7.5.9 to 7.5.10 in /frontend by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3075 * Bump joserfc from 1.3.5 to 1.6.3 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3064 * Bump tornado from 6.5.1 to 6.5.5 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3123 * chore(deps): bump authlib from 1.6.7 to 1.6.9 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3138 * chore(deps): bump memray from 1.17.2 to 1.19.2 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3141 * chore(deps): bump socket.io-parser from 4.2.5 to 4.2.6 in /frontend by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3144 * chore(deps-dev): bump flatted from 3.3.4 to 3.4.2 in /frontend by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3146 * chore(deps-dev): bump picomatch from 2.3.1 to 2.3.2 in /frontend by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3177 * chore(deps): bump requests from 2.32.4 to 2.33.0 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3186 * chore(deps-dev): bump handlebars from 4.7.8 to 4.7.9 in /frontend by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3188 * chore(deps): bump cryptography from 46.0.5 to 46.0.6 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3196 * chore(deps): bump pygments from 2.19.2 to 2.20.0 by @&#8203;dependabot[bot] in https://github.com/rommapp/romm/pull/3199 > \[!NOTE] > **Environment Variables** > > `UPLOAD_TIMEOUT` has been removed in favour of chunked uploads > `OAUTH_ACCESS_TOKEN_EXPIRE_SECONDS`: Access token lifetime (in seconds, default 30 minutes) > `OAUTH_REFRESH_TOKEN_EXPIRE_SECONDS`: Refresh token lifetime (in seconds, default 7 days) > `INVITE_TOKEN_EXPIRY_SECONDS`: Invite token lifetime (in seconds, default 10 minutes) > `OIDC_SERVER_METADATA_URL`: URL to the OIDC provider metadata endpoint > `OIDC_RP_INITIATED_LOGOUT`: Enable RP-initiated logout flow (default `false`) > `OIDC_END_SESSION_ENDPOINT`: OIDC end-session endpoint override URL #### New Contributors - [@&#8203;gravelfreeman](https://github.com/gravelfreeman) made their first contribution in [#&#8203;2922](https://github.com/rommapp/romm/pull/2922) - [@&#8203;Drarox](https://github.com/Drarox) made their first contribution in [#&#8203;3060](https://github.com/rommapp/romm/pull/3060) - [@&#8203;alanfrigo](https://github.com/alanfrigo) made their first contribution in [#&#8203;3076](https://github.com/rommapp/romm/pull/3076) - [@&#8203;pacnpal](https://github.com/pacnpal) made their first contribution in [#&#8203;3103](https://github.com/rommapp/romm/pull/3103) - [@&#8203;cciollaro](https://github.com/cciollaro) made their first contribution in [#&#8203;3104](https://github.com/rommapp/romm/pull/3104) - [@&#8203;HydroSulphide](https://github.com/HydroSulphide) made their first contribution in [#&#8203;3102](https://github.com/rommapp/romm/pull/3102) - [@&#8203;Bergbok](https://github.com/Bergbok) made their first contribution in [#&#8203;3121](https://github.com/rommapp/romm/pull/3121) **Full Changelog**: <https://github.com/rommapp/romm/compare/4.7.0...4.8.0> ### [`v4.7.0`](https://github.com/rommapp/romm/releases/tag/4.7.0) [Compare Source](https://github.com/rommapp/romm/compare/4.6.1...4.7.0) #### Highlights ##### Auto-export `gamelist.xml` Automatically generate an ES-DE compatible `gamelist.xml` file during scans by enabling the option in your `config.yml`. This will place the new `gamelist.xml` file within the platform folder for selected/discovered platforms. ```yaml scan: export_gamelist: true ``` [@&#8203;ItzCobaltboy](https://github.com/ItzCobaltboy) in [#&#8203;2987](https://github.com/rommapp/romm/issues/2987) ##### Device-based save synchronization Thanks to [@&#8203;tmgast](https://github.com/tmgast), we've laid the ground work for device-based save synchronization to enable multi-device save management, as defined in [RFC-0001](https://github.com/rommapp/romm/discussions/2199#discussioncomment-14450967). Devices (handhelds, PCs, etc.) can register with the server and track which saves they've synced, enabling conflict detection when a device tries to upload stale data. Note that this is **purely foundational work** to set up an initial structure and API implementation and in intended for third-party developers. [#&#8203;2917](https://github.com/rommapp/romm/issues/2917) #### Minor changes - Add support for [ScummVM shortnames](https://scummvm.org/compatibility/2.9.1/) by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2957](https://github.com/rommapp/romm/pull/2957) - Add `OIDC_AUTOLOGIN=true` env var to automatically login using OIDC by [@&#8203;pR0Ps](https://github.com/pR0Ps) in [#&#8203;2969](https://github.com/rommapp/romm/pull/2969) - Enable keyboard lock in emulatorjs by [@&#8203;matthewturk](https://github.com/matthewturk) in [#&#8203;3043](https://github.com/rommapp/romm/pull/3043) - Group games with same name (but different tags) as versions/siblings by [@&#8203;zeroSteiner](https://github.com/zeroSteiner) in [#&#8203;2984](https://github.com/rommapp/romm/pull/2984) - Search by mutliple keywords with `|` by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2996](https://github.com/rommapp/romm/pull/2996) - `zelda|mario|sonic` would show all games with `zelda`, `mario` OR `sonic` in the name - New `PKGJ` feed endpoints in `.tsv` format by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2986](https://github.com/rommapp/romm/pull/2986) - PSP games: `GET /api/feeds/pkgj/psp/games` - PSP DLC files: `GET /api/feeds/pkgj/psp/dlc` - PS Vita games: `GET /api/feeds/pkgj/psvita/games` - PS Vita DLC files: `GET /api/feeds/pkgj/psvita/dlc` - PSX games: `GET /api/feeds/pkgj/psx/games` #### Fixes - \[HOTFIX] Fix all roms displayed in all platforms by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2948](https://github.com/rommapp/romm/pull/2948) - \[ROMM-2956] Fix refresh on card edit by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2962](https://github.com/rommapp/romm/pull/2962) - Invalid category values fix by [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) in [#&#8203;2979](https://github.com/rommapp/romm/pull/2979) - Filter keys now plural by [@&#8203;camhorn](https://github.com/camhorn) in [#&#8203;2982](https://github.com/rommapp/romm/pull/2982) - \[ROMM-2991] Allow set completion to 0 by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2995](https://github.com/rommapp/romm/pull/2995) - \[ROMM-2997] Fix HLTB search term fetching by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2998](https://github.com/rommapp/romm/pull/2998) - \[ROMM-2983] Fix negative filter by verified by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3000](https://github.com/rommapp/romm/pull/3000) - \[ROMM-2976] Fix no games displayed for status filter by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3001](https://github.com/rommapp/romm/pull/3001) - \[ROMM-2974] Stop displaying public collections in "add to collection" dialog the user doesn't own by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3002](https://github.com/rommapp/romm/pull/3002) - \[ROMM-2972] Fix /props returning stale data by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3003](https://github.com/rommapp/romm/pull/3003) - \[ROMM-2965] Check file count for file select menu emulatorjs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3005](https://github.com/rommapp/romm/pull/3005) - \[ROMM-2943] Match scanned platform based on fs\_slug by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3008](https://github.com/rommapp/romm/pull/3008) - \[ROMM-3020] Fix split collection custom artowrk by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3028](https://github.com/rommapp/romm/pull/3028) - \[ROMM-3025] Fix collections with custom art failing by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3027](https://github.com/rommapp/romm/pull/3027) - \[ROMM-3026] Region/language shortcodes should be case sensitive by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3035](https://github.com/rommapp/romm/pull/3035) - \[ROMM-3031] Fix screenshot property on save/state by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3037](https://github.com/rommapp/romm/pull/3037) - Remove ICE server list from config response when unauthenticated by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3054](https://github.com/rommapp/romm/pull/3054) #### Other changes - New endpoints to fetch all IDs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2921](https://github.com/rommapp/romm/pull/2921) - Example config ES-DE update/fix by [@&#8203;ricardovdheijden](https://github.com/ricardovdheijden) in [#&#8203;2963](https://github.com/rommapp/romm/pull/2963) - Use vite copy plugin to load rom-patcher by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2964](https://github.com/rommapp/romm/pull/2964) - Skip opentelemetry-instrument when OTEL is disabled by [@&#8203;Wadenschwinger](https://github.com/Wadenschwinger) in [#&#8203;2968](https://github.com/rommapp/romm/pull/2968) - Update uses of roms to ROMs by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3009](https://github.com/rommapp/romm/pull/3009) - \[ROMM-2927] Add default folders to config manager by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3007](https://github.com/rommapp/romm/pull/3007) - \[AIKIDO-13126609] Raise HTTP exception if device not found by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3030](https://github.com/rommapp/romm/pull/3030) - \[AIKIDO-13126604] Stream file when building file hash by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3029](https://github.com/rommapp/romm/pull/3029) - Ugprade some docker base images by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3032](https://github.com/rommapp/romm/pull/3032) - Run formatter on alembic files by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3034](https://github.com/rommapp/romm/pull/3034) - Add Rom.ra\_hash and RomFile.ra\_hash fields to API by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3019](https://github.com/rommapp/romm/pull/3019) - Add platforms icons for neogeox, neogeoaes, appleii-gs and vsmile by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;3017](https://github.com/rommapp/romm/pull/3017) - Fix Grout author and add GH profile links by [@&#8203;BrandonKowalski](https://github.com/BrandonKowalski) in [#&#8203;3046](https://github.com/rommapp/romm/pull/3046) - Bump tar from 7.5.6 to 7.5.9 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2954](https://github.com/rommapp/romm/pull/2954) and [#&#8203;3039](https://github.com/rommapp/romm/pull/3039) - Bump protobuf from 6.31.1 to 6.33.5 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2981](https://github.com/rommapp/romm/pull/2981) - Bump cryptography from 45.0.5 to 46.0.5 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3013](https://github.com/rommapp/romm/pull/3013) - Bump axios from 1.12.1 to 1.13.5 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3010](https://github.com/rommapp/romm/pull/3010) - Bump pillow from 10.4.0 to 12.1.1 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3015](https://github.com/rommapp/romm/pull/3015) - Bump markdown-it from 14.1.0 to 14.1.1 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3022](https://github.com/rommapp/romm/pull/3022) - Bump rollup in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3049](https://github.com/rommapp/romm/pull/3049) - Bump minimatch in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;3050](https://github.com/rommapp/romm/pull/3050) #### New Contributors - [@&#8203;ricardovdheijden](https://github.com/ricardovdheijden) made their first contribution in [#&#8203;2963](https://github.com/rommapp/romm/pull/2963) - [@&#8203;Wadenschwinger](https://github.com/Wadenschwinger) made their first contribution in [#&#8203;2968](https://github.com/rommapp/romm/pull/2968) - [@&#8203;pR0Ps](https://github.com/pR0Ps) made their first contribution in [#&#8203;2969](https://github.com/rommapp/romm/pull/2969) - [@&#8203;LoneAngelFayt](https://github.com/LoneAngelFayt) made their first contribution in [#&#8203;2979](https://github.com/rommapp/romm/pull/2979) - [@&#8203;camhorn](https://github.com/camhorn) made their first contribution in [#&#8203;2982](https://github.com/rommapp/romm/pull/2982) - [@&#8203;tmgast](https://github.com/tmgast) made their first contribution in [#&#8203;2917](https://github.com/rommapp/romm/pull/2917) - [@&#8203;zeroSteiner](https://github.com/zeroSteiner) made their first contribution in [#&#8203;2984](https://github.com/rommapp/romm/pull/2984) - [@&#8203;ItzCobaltboy](https://github.com/ItzCobaltboy) made their first contribution in [#&#8203;2987](https://github.com/rommapp/romm/pull/2987) - [@&#8203;matthewturk](https://github.com/matthewturk) made their first contribution in [#&#8203;3043](https://github.com/rommapp/romm/pull/3043) **Full Changelog**: <https://github.com/rommapp/romm/compare/4.6.1...4.7.0> ### [`v4.6.1`](https://github.com/rommapp/romm/releases/tag/4.6.1) [Compare Source](https://github.com/rommapp/romm/compare/4.6.0...4.6.1) #### What's Changed - \[HOTFIX] Fix 0063 migration postgres by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2940](https://github.com/rommapp/romm/pull/2940) - Bump python-multipart from 0.0.20 to 0.0.22 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2939](https://github.com/rommapp/romm/pull/2939) - Add workflow to run migrations on PR by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2941](https://github.com/rommapp/romm/pull/2941) - Add match-none-logig entries to locales by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2944](https://github.com/rommapp/romm/pull/2944) **Full Changelog**: <https://github.com/rommapp/romm/compare/4.6.0...4.6.1> ### [`v4.6.0`](https://github.com/rommapp/romm/releases/tag/4.6.0) [Compare Source](https://github.com/rommapp/romm/compare/4.5.0...4.6.0) #### Highlights ##### Additional metadata The edit dialog now allows you to manually set metadata properties that would otherwise only be aggregated from metadata sources, if those metadata sources don't support/populate certain fields. [#&#8203;2848](https://github.com/rommapp/romm/issues/2848) <img width="977" height="489" alt="Screenshot 2026-01-12 at 1 16 34 PM" src="https://github.com/user-attachments/assets/d706a8e8-1e09-403c-ba48-1ca5923476f3" /> ##### Enhance folder structure config We've improved the web interface that lets you configure platform bindings and versions, and excluded files and folders. This change should simplify the process of configuring how library folders are interpreted and what content is ignored during scanning. [#&#8203;2809](https://github.com/rommapp/romm/issues/2809) | <img width="961" height="713" alt="Screenshot 2026-01-12 at 12 25 11 PM" src="https://github.com/user-attachments/assets/00de8734-e2b5-4bca-a450-182ff2229fb7" /> | <img width="964" height="683" alt="Screenshot 2026-01-12 at 12 25 23 PM" src="https://github.com/user-attachments/assets/c1303626-d421-4c8a-939c-825c72db8fe5" /> | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ##### Patcher.js - We're introducing a new ROM patching feature to the app that enables your admins and editors to apply various types of patches to ROM files. At this time both ROM and patch files must be downloaded locally and re-uploaded via the patcher, with future work planned to patch ROMs directly from your library. [#&#8203;2787](https://github.com/rommapp/romm/issues/2787) <img width="926" height="741" alt="Screenshot 2026-01-12 at 12 25 37 PM" src="https://github.com/user-attachments/assets/7e5b231a-4ed3-4736-9f05-6f28f45c1ddb" /> ##### UI settings sync UI settings are now synced between devices for every user! These settings do not affect third-party apps, and *any setting* must be changed *at least once* for this sync to take effect. [#&#8203;2777](https://github.com/rommapp/romm/issues/2777) ##### BIOS file defaults A default BIOS file can now be specified per emulator core directly in your `config.yml`: ```yaml emulatorjs: settings: mgba: bios_file: gba_bios.bin ``` [#&#8203;2873](https://github.com/rommapp/romm/issues/2873) #### Minor changes - Add library structure management on setup wizard by [@&#8203;zurdi15](https://github.com/zurdi15) [#&#8203;2784](https://github.com/rommapp/romm/issues/2784) - Added player count metadata from SS with display and filter by [@&#8203;DevOldSchool](https://github.com/DevOldSchool) in <https://github.com/rommapp/romm/2872> - Add support for version tag from filename by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2820](https://github.com/rommapp/romm/pull/2820) - Support for multi-value filters by [@&#8203;adamantike](https://github.com/adamantike) in [#&#8203;2411](https://github.com/rommapp/romm/pull/2411) - Allow chd lookups in the hasheous database by [@&#8203;josegonzalez](https://github.com/josegonzalez) in [#&#8203;2866](https://github.com/rommapp/romm/pull/2866) - ROMs filter endpoint + refactor by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2904](https://github.com/rommapp/romm/pull/2904) - Add NONE support for filter logic operators by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2933](https://github.com/rommapp/romm/pull/2933) - Update platform display in Scan.vue to use display\_name and enhance item details by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;2935](https://github.com/rommapp/romm/pull/2935) #### Fixes - Fix YAML indentation in netplay section of config example file by [@&#8203;Zuko](https://github.com/Zuko) in [#&#8203;2812](https://github.com/rommapp/romm/pull/2812) - \[HOTFIX] Fix scrolling load more in gallery views by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2813](https://github.com/rommapp/romm/pull/2813) - \[HOTFIX] Multi roms should only consider top-level files by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2821](https://github.com/rommapp/romm/pull/2821) - Notes flag by [@&#8203;zurdi15](https://github.com/zurdi15) in [#&#8203;2817](https://github.com/rommapp/romm/pull/2817) - \[ROMM-2824] Also replace - with : for edge cases in HLTB and LB handlers by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2827](https://github.com/rommapp/romm/pull/2827) - \[ROMM-2844] Use webp images for game covers in list view by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2845](https://github.com/rommapp/romm/pull/2845) - \[ROMM-2835] Fix appearance of chevron screenshot buttons by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2846](https://github.com/rommapp/romm/pull/2846) - \[ROMM-2833] Fix using default locales in igdb handler by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2847](https://github.com/rommapp/romm/pull/2847) - \[ROMM-2796] Add check to overwrite media assets by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2851](https://github.com/rommapp/romm/pull/2851) - Allow deleting single chip in additional metadata by [@&#8203;SaraVieira](https://github.com/SaraVieira) in [#&#8203;2861](https://github.com/rommapp/romm/pull/2861) - \[ROMM-2853] Update rom\_file.category column enum by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2860](https://github.com/rommapp/romm/pull/2860) - \[HOTFIX] Dont schedule tasks if already queued by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2885](https://github.com/rommapp/romm/pull/2885) - \[ROMM-2808] Stop filtering nsfw games from search results by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2852](https://github.com/rommapp/romm/pull/2852) - \[ROMM-2856] Use platform slug to fetch feed games by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2862](https://github.com/rommapp/romm/pull/2862) - \[HOTFIX] Check if gamelist metadata source selected before populating cache by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2865](https://github.com/rommapp/romm/pull/2865) - \[ROMM-2889] Add media to update\_config\_file by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2897](https://github.com/rommapp/romm/pull/2897) - \[HOTFIX] Rename selected\_statuses to statuses by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2902](https://github.com/rommapp/romm/pull/2902) - \[HOTFIX] Only add ? to qs on download path by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2907](https://github.com/rommapp/romm/pull/2907) - \[ROMM-2874] Feature/updated-after-query-param by [@&#8203;BrandonKowalski](https://github.com/BrandonKowalski) in [#&#8203;2898](https://github.com/rommapp/romm/pull/2898) - \[ROMM-2906] Fix icon color light mode by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2913](https://github.com/rommapp/romm/pull/2913) - \[HOTFIX] Remove `files` from model\_dump in scan by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2914](https://github.com/rommapp/romm/pull/2914) - \[ROMM-2891] Empty String Flashpoint ID by [@&#8203;BrandonKowalski](https://github.com/BrandonKowalski) in [#&#8203;2918](https://github.com/rommapp/romm/pull/2918) #### Other changes - Improve performance of queries on roms table by [@&#8203;MaienM](https://github.com/MaienM) in [#&#8203;2811](https://github.com/rommapp/romm/pull/2811) - Add CPS1-3 and TIC-80 by [@&#8203;SaraVieira](https://github.com/SaraVieira) in [#&#8203;2828](https://github.com/rommapp/romm/pull/2828) - Update docker compose variables by [@&#8203;SaraVieira](https://github.com/SaraVieira) in [#&#8203;2849](https://github.com/rommapp/romm/pull/2849) - Configure and run deptry by [@&#8203;adamantike](https://github.com/adamantike) in [#&#8203;2859](https://github.com/rommapp/romm/pull/2859) - \[HOTFIX] Set metadata fields as nullable by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2864](https://github.com/rommapp/romm/pull/2864) - \[ROMM-2877] Drop index on rom file content by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2878](https://github.com/rommapp/romm/pull/2878) - Add `WEB_SERVER_GUNICORN_WAIT` env var for slow systems by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2887](https://github.com/rommapp/romm/pull/2887) - Collection updated after query param by [@&#8203;BrandonKowalski](https://github.com/BrandonKowalski) in [#&#8203;2915](https://github.com/rommapp/romm/pull/2915) - Decompress 7z archives on the fly rather than extracting to disk by [@&#8203;Delgan](https://github.com/Delgan) in [#&#8203;2931](https://github.com/rommapp/romm/pull/2931) - Add CHEAT to RomFile category types by [@&#8203;gantoine](https://github.com/gantoine) in [#&#8203;2937](https://github.com/rommapp/romm/pull/2937) - Bump tar from 7.4.3 to 7.5.3 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2908](https://github.com/rommapp/romm/pull/2908) - Bump authlib from 1.6.5 to 1.6.6 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2909](https://github.com/rommapp/romm/pull/2909) - Bump urllib3 from 2.6.2 to 2.6.3 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2910](https://github.com/rommapp/romm/pull/2910) - Bump aiohttp from 3.12.14 to 3.13.3 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2911](https://github.com/rommapp/romm/pull/2911) - Bump python-socketio from 5.11.1 to 5.14.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2912](https://github.com/rommapp/romm/pull/2912) - Bump tar from 7.5.3 to 7.5.6 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2929](https://github.com/rommapp/romm/pull/2929) - Bump lodash from 4.17.21 to 4.17.23 in /frontend by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;2932](https://github.com/rommapp/romm/pull/2932) #### New Contributors - [@&#8203;Zuko](https://github.com/Zuko) made their first contribution in [#&#8203;2812](https://github.com/rommapp/romm/pull/2812) - [@&#8203;josegonzalez](https://github.com/josegonzalez) made their first contribution in [#&#8203;2866](https://github.com/rommapp/romm/pull/2866) - [@&#8203;DevOldSchool](https://github.com/DevOldSchool) made their first contribution in [#&#8203;2872](https://github.com/rommapp/romm/pull/2872) - [@&#8203;BrandonKowalski](https://github.com/BrandonKowalski) made their first contribution in [#&#8203;2898](https://github.com/rommapp/romm/pull/2898) - [@&#8203;Delgan](https://github.com/Delgan) made their first contribution in [#&#8203;2931](https://github.com/rommapp/romm/pull/2931) **Full Changelog**: <https://github.com/rommapp/romm/compare/4.5.0...4.6.0> ### [`v4.5.0`](https://github.com/rommapp/romm/releases/tag/4.5.0) [Compare Source](https://github.com/rommapp/romm/compare/4.4.1...4.5.0) ### Highlights #### Netplay The incredibly smart and beautiful people over at the [EmulatorJS](https://emulatorjs.org/) project have added Netplay to their client, allowing you to play with your friends on the same instance over the internet! Enable netplay and configure the STUN/TURN servers in your `config.yml`: ```yaml emulatorjs: netplay: enabled: true ice_servers: - urls: "stun:stun.relay.metered.ca:80" - urls: "turn:global.relay.metered.ca:80" username: "<username>" credential: "<password>" ``` To host a game, start it, then hit the 🌐 icon in botton bar. Set your name, create a room (password optional), and other players should be able to see and join your room. **All players need access to your RomM server to join a room and play together.** [#&#8203;2728](https://github.com/rommapp/romm/issues/2728) #### Multiple notes You've begged for it, and boy did we ever delivered! Write and store multiple notes for each game, with full mark down support; sharable, swapable, and easily identified with the 📝 icon. [#&#8203;2665](https://github.com/rommapp/romm/issues/2665) <img width="1478" height="679" alt="Screenshot 2025-12-10 at 3 52 36 PM" src="https://github.com/user-attachments/assets/ccc5b16e-d845-4b80-8c2a-cc04183d3f01" /> #### Metadata locale selection for IGDB and ScreenScraper You can now set the order of your preferred locales/regional variants when fetching metadata from IGDB and ScreenScraper. The options below are available and can be set in your `config.yml`. [#&#8203;2700](https://github.com/rommapp/romm/issues/2700) ```yaml scan: priority: region: - "us" # United States (no localization needed) - "wor" # World (default) - "ss" # Screenscraper (default) - "eu" # Europe - "jp" # Japan - "kr" # Korea - "cn" # China (simplified) - "tw" # Taiwan (traditional) ``` <img width="996" height="541" alt="516566290-2c43d67e-f55e-400b-8a89-a23e25d1ef36" src="https://github.com/user-attachments/assets/4b4d97c2-43e3-4c7a-aed0-bd9dfd689cb3" /> #### Minor changes * Query parameter handling for tabs and sub-tabs by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/2695 * Redirect related games to RomM entry if exists by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/2729 * Make hashing during scan optional with `filesystem.skip_hash_calculation` in `config.yml` by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/2703 * Console mode locale by @&#8203;Slabak007 in https://github.com/rommapp/romm/pull/2710 * Add translations for firmware management dialogs by @&#8203;tvdu29 in https://github.com/rommapp/romm/pull/2714 * Hungarian translation added (finish, 12/12 files done). by @&#8203;Soderes in https://github.com/rommapp/romm/pull/2718 * Improved save/state layout by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/2779 * [ROMM-2697] Use colocated es-de folders to import art not present in gamelist.xml by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2766 #### Fixes * Czech language pluralization by @&#8203;Slabak007 in https://github.com/rommapp/romm/pull/2701 * Fixed link to documentation in filestructure error by @&#8203;Descent098 in https://github.com/rommapp/romm/pull/2713 * Fix French translations and add locale-aware date formatting by @&#8203;tvdu29 in https://github.com/rommapp/romm/pull/2740 * Invalidate invite token before creating user by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2715 * Fix kekatsu tsv format by @&#8203;phramusca in https://github.com/rommapp/romm/pull/2719 * Update /gamelist/export to generate a correct Batocera gamelist.xml by @&#8203;vargash in https://github.com/rommapp/romm/pull/2720 * Migration 0057 fix by @&#8203;Tux00-repo in https://github.com/rommapp/romm/pull/2739 * [ROMM-2746] Drop + from filenames on update by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2752 * [ROMM-2748] Add `OIDC_USERNAME_ATTRIBUTE` by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2755 * [ROMM-2721] Fix saving user without password by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2754 * [ROMM-2722] Fix matching special chars in ssfr by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2753 * Catch RomsNotFoundException in scan file precalculation by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2756 * [ROMM-2685] Fix updating ssfr ID art assets by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2758 * Strip keys from ssfr api query params by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2778 * Change scan type to QUICK for scheduled library scans by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/2782 * [HOTFIX] Fetch security token for HLTB by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2772 * [ROMM-2774] Remove unsupported language tags by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2775 * [ROMM-2791] Stop filtering out None values in order_by filter by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2804 * [ROMM-2806] Fix arcade ssid in screenscraper by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2807 * Game details view little fixes by @&#8203;zurdi15 in https://github.com/rommapp/romm/pull/2803 #### Behind-the-scenes * Cache parsed gamelist.xml files on each scan by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2699 * Add redis-backed session middleware by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2716 * Fix scan_rom tests by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2731 * Move merged_screenshots and merged_ra_metadata to simple RomSchema by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2744 * [ROMM-2668] Speedup feeds endpoint by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2757 * [ROMM-2762] Add get_rom_by_hash endpoint by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2764 * [ROMM-2763] Add MSX2 to ssfr platforms list by @&#8203;gantoine in https://github.com/rommapp/romm/pull/2765 * Add OpenBOR metadata entry for screenscraper by @&#8203;ronzano in https://github.com/rommapp/romm/pull/2760 #### New Contributors * @&#8203;Descent098 made their first contribution in https://github.com/rommapp/romm/pull/2713 * @&#8203;phramusca made their first contribution in https://github.com/rommapp/romm/pull/2719 * @&#8203;vargash made their first contribution in https://github.com/rommapp/romm/pull/2720 * @&#8203;Soderes made their first contribution in https://github.com/rommapp/romm/pull/2718 * @&#8203;Tux00-repo made their first contribution in https://github.com/rommapp/romm/pull/2739 * @&#8203;ronzano made their first contribution in https://github.com/rommapp/romm/pull/2760 **Full Changelog**: <https://github.com/rommapp/romm/compare/4.4.1...4.5.0> </details> --- ### Configuration 📅 **Schedule**: (UTC) - Branch creation - At any time (no schedule defined) - Automerge - At any time (no schedule defined) 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yNTMuMSIsInVwZGF0ZWRJblZlciI6IjQzLjI1My4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
renovate-bot changed title from Update ghcr.io/rommapp/romm Docker tag to v5 to chore(deps): update ghcr.io/rommapp/romm docker tag to v5 2026-07-17 23:16:01 +00:00
renovate-bot changed title from chore(deps): update ghcr.io/rommapp/romm docker tag to v5 to Update ghcr.io/rommapp/romm Docker tag to v5 2026-07-21 01:12:08 +00:00
renovate-bot changed title from Update ghcr.io/rommapp/romm Docker tag to v5 to chore(deps): update ghcr.io/rommapp/romm docker tag to v5 2026-07-21 02:12:05 +00:00
renovate-bot force-pushed renovate/ghcr.io-rommapp-romm-5.x from 0c45155d59 to bc2c3b510d 2026-09-21 19:22:54 +00:00 Compare
renovate-bot force-pushed renovate/ghcr.io-rommapp-romm-5.x from bc2c3b510d to 9b45a743a6
Some checks failed
ci/crow/pr/dns-test Pipeline was successful
ci/crow/push/tanka-test Pipeline failed
ci/crow/pr/tanka-test Pipeline was successful
ci/crow/push/dns-test Pipeline was successful
ci/crow/pr/tanka-diff Pipeline was successful
2026-09-23 04:13:38 +00:00
Compare
Member

flate diff

No differences detected.

Pipeline

## flate diff No differences detected. [Pipeline](https://ci.aether.earth/repos/4/pipeline/373)
renovate-bot force-pushed renovate/ghcr.io-rommapp-romm-5.x from 9b45a743a6
Some checks failed
ci/crow/pr/dns-test Pipeline was successful
ci/crow/push/tanka-test Pipeline failed
ci/crow/pr/tanka-test Pipeline was successful
ci/crow/push/dns-test Pipeline was successful
ci/crow/pr/tanka-diff Pipeline was successful
to a1cd32f9ab
All checks were successful
ci/crow/push/dns-test Pipeline was successful
ci/crow/pr/dns-test Pipeline was successful
ci/crow/push/tanka-test Pipeline was successful
ci/crow/pr/tanka-test Pipeline was successful
ci/crow/pr/tanka-diff Pipeline was successful
2026-10-10 16:12:21 +00:00
Compare
Member

flate diff

rules  (rbac.authorization.k8s.io/v1/ClusterRole/kube-prometheus-stack-operator)
  - six list entries removed:           + three list entries added:
    - resources:                          - resources:
      - alertmanagers                       - alertmanagers
      - prometheusagents                    - alertmanagers/finalizers
      - prometheuses                        - alertmanagers/status
      - thanosrulers                        - alertmanagerconfigs
      apiGroups:                            - prometheuses
      - monitoring.coreos.com               - prometheuses/finalizers
      verbs:                                - prometheuses/status
      - patch                               - prometheusagents
    - resources:                            - prometheusagents/finalizers
      - alertmanagers/status                - prometheusagents/status
      - podmonitors/status                  - thanosrulers
      - probes/status                       - thanosrulers/finalizers
      - prometheuses/status                 - thanosrulers/status
      - prometheusagents/status             - scrapeconfigs
      - prometheusrules/status              - scrapeconfigs/status
      - scrapeconfigs/status                - servicemonitors
      - servicemonitors/status              - servicemonitors/status
      - thanosrulers/status                 - podmonitors
      apiGroups:                            - podmonitors/status
      - monitoring.coreos.com               - probes
      verbs:                                - probes/status
      - create                              - prometheusrules
      - update                              - prometheusrules/status
      - patch                               apiGroups:
      - delete                              - monitoring.coreos.com
    - resources:                            verbs:
      - alertmanagers/finalizers            - "*"
      - prometheusagents/finalizers       - resources:
      - prometheuses/finalizers             - statefulsets
      - thanosrulers/finalizers             apiGroups:
      apiGroups:                            - apps
      - monitoring.coreos.com               verbs:
      verbs:                                - "*"
      - update                            - resources:
    - resources:                            - configmaps
      - alertmanagers                       - secrets
      - alertmanagerconfigs                 apiGroups:
      - podmonitors                         -
      - probes                              verbs:
      - prometheusagents                    - "*"
      - prometheuses
      - prometheusrules
      - servicemonitors
      - scrapeconfigs
      - thanosrulers
      apiGroups:
      - monitoring.coreos.com
      verbs:
      - get
      - list
      - watch
    - resources:
      - statefulsets
      apiGroups:
      - apps
      verbs:
      - get
      - list
      - watch
      - create
      - update
      - patch
      - delete
    - resources:
      - configmaps
      - secrets
      apiGroups:
      -
      verbs:
      - get
      - list
      - watch
      - create
      - update
      - delete

metadata  (v1/Service/kube-privileged/node-exporter)
  + one map entry added:
    annotations:
      prometheus.io/scrape: "true"

spec.template.spec.containers.kube-state-metrics.image  (apps/v1/Deployment/monitoring/kube-state-metrics)
  ± value change
    - registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.20.0
    + registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.19.1

spec.template.spec  (apps/v1/Deployment/monitoring/kube-prometheus-stack-operator)
  - one map entry removed:
    nodeSelector:
      kubernetes.io/os: linux

spec.template.spec.containers.kube-prometheus-stack.args  (apps/v1/Deployment/monitoring/kube-prometheus-stack-operator)
  - three list entries removed:
    - "--prometheus-config-reloader=quay.io/prometheus-operator/prometheus-config-reloader:v0.94.1"
    - "--thanos-default-base-image=quay.io/thanos/thanos:v0.42.4"
    - "--secret-field-selector=type!=kubernetes.io/dockercfg,type!=helm.sh/release.v1"
    
  
  + three list entries added:
    - "--prometheus-config-reloader=quay.io/prometheus-operator/prometheus-config-reloader:v0.92.1"
    - "--thanos-default-base-image=quay.io/thanos/thanos:v0.42.2"
    - "--secret-field-selector=type!=kubernetes.io/dockercfg,type!=kubernetes.io/service-account-token,type!=helm.sh/release.v1"
    
  

spec.template.spec.containers.kube-prometheus-stack.image  (apps/v1/Deployment/monitoring/kube-prometheus-stack-operator)
  ± value change
    - quay.io/prometheus-operator/prometheus-operator:v0.94.1
    + quay.io/prometheus-operator/prometheus-operator:v0.92.1

spec  (monitoring.coreos.com/v1/Alertmanager/monitoring/kube-prometheus-stack)
  - one map entry removed:        + one map entry added:
    nodeSelector:                   hostNetwork: false
      kubernetes.io/os: linux

spec.image  (monitoring.coreos.com/v1/Alertmanager/monitoring/kube-prometheus-stack)
  ± value change
    - quay.io/prometheus/alertmanager:v0.34.1
    + quay.io/prometheus/alertmanager:v0.33.1

spec.version  (monitoring.coreos.com/v1/Alertmanager/monitoring/kube-prometheus-stack)
  ± value change
    - v0.34.1
    + v0.33.1

spec  (monitoring.coreos.com/v1/Prometheus/monitoring/kube-prometheus-stack)
  - one map entry removed:
    nodeSelector:
      kubernetes.io/os: linux

spec.image  (monitoring.coreos.com/v1/Prometheus/monitoring/kube-prometheus-stack)
  ± value change
    - quay.io/prometheus/prometheus:v3.15.0-distroless
    + quay.io/prometheus/prometheus:v3.13.1-distroless

spec.version  (monitoring.coreos.com/v1/Prometheus/monitoring/kube-prometheus-stack)
  ± value change
    - v3.15.0-distroless
    + v3.13.1-distroless

spec.groups.kube-apiserver-burnrate.rules.rules  (monitoring.coreos.com/v1/PrometheusRule/monitoring/kube-prometheus-stack-kube-apiserver-burnrate.rules)
  - eleven list entries removed:
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward"}[5m]))
            -
            (
              (
                sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope=~"resource|",le=~"1(\\.0)?"}[5m]))
                or
                vector(0)
              )
              +
              sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="namespace",le=~"5(\\.0)?"}[5m]))
              +
              sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="cluster",le=~"30(\\.0)?"}[5m]))
            )
          )
          +
          # errors
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET",code=~"5.."}[5m]))
        )
      labels:
        verb: read
      record: "cluster_verb:apiserver_request_sli_bad_events:rate5m"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[5m]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[5m]))
          )
          +
          # errors
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[5m]))
        )
      labels:
        verb: write
      record: "cluster_verb:apiserver_request_sli_bad_events:rate5m"
    - expr: "sum by (cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[5m]))"
      labels:
        verb: read
      record: "cluster_verb:apiserver_request_sli_events:rate5m"
    - expr: "sum by (cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[5m]))"
      labels:
        verb: write
      record: "cluster_verb:apiserver_request_sli_events:rate5m"
    - expr: |
        cluster_verb:apiserver_request_sli_bad_events:rate5m
        /
        cluster_verb:apiserver_request_sli_events:rate5m
      record: "apiserver_request:burnrate5m"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[1h]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[1h]))
          )
          +
          # errors
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[1h]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[1h]))
      labels:
        verb: write
      record: "apiserver_request:burnrate1h"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[2h]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[2h]))
          )
          +
          # errors
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[2h]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[2h]))
      labels:
        verb: write
      record: "apiserver_request:burnrate2h"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[30m]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[30m]))
          )
          +
          # errors
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[30m]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[30m]))
      labels:
        verb: write
      record: "apiserver_request:burnrate30m"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[6h]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[6h]))
          )
          +
          # errors
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[6h]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[6h]))
      labels:
        verb: write
      record: "apiserver_request:burnrate6h"
    - expr: |
        avg_over_time(cluster_verb:apiserver_request_sli_bad_events:rate5m[1d])
        /
        avg_over_time(cluster_verb:apiserver_request_sli_events:rate5m[1d])
      record: "apiserver_request:burnrate1d"
    - expr: |
        avg_over_time(cluster_verb:apiserver_request_sli_bad_events:rate5m[3d])
        /
        avg_over_time(cluster_verb:apiserver_request_sli_events:rate5m[3d])
      record: "apiserver_request:burnrate3d"
    
  
  + ten list entries added:
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward"}[1d]))
            -
            (
              (
                sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope=~"resource|",le=~"1(\\.0)?"}[1d]))
                or
                vector(0)
              )
              +
              sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="namespace",le=~"5(\\.0)?"}[1d]))
              +
              sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="cluster",le=~"30(\\.0)?"}[1d]))
            )
          )
          +
          # errors
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET",code=~"5.."}[1d]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET"}[1d]))
      labels:
        verb: read
      record: "apiserver_request:burnrate1d"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward"}[3d]))
            -
            (
              (
                sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope=~"resource|",le=~"1(\\.0)?"}[3d]))
                or
                vector(0)
              )
              +
              sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="namespace",le=~"5(\\.0)?"}[3d]))
              +
              sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="cluster",le=~"30(\\.0)?"}[3d]))
            )
          )
          +
          # errors
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET",code=~"5.."}[3d]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET"}[3d]))
      labels:
        verb: read
      record: "apiserver_request:burnrate3d"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward"}[5m]))
            -
            (
              (
                sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope=~"resource|",le=~"1(\\.0)?"}[5m]))
                or
                vector(0)
              )
              +
              sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="namespace",le=~"5(\\.0)?"}[5m]))
              +
              sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="cluster",le=~"30(\\.0)?"}[5m]))
            )
          )
          +
          # errors
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET",code=~"5.."}[5m]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET"}[5m]))
      labels:
        verb: read
      record: "apiserver_request:burnrate5m"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[1d]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[1d]))
          )
          +
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[1d]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[1d]))
      labels:
        verb: write
      record: "apiserver_request:burnrate1d"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[1h]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[1h]))
          )
          +
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[1h]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[1h]))
      labels:
        verb: write
      record: "apiserver_request:burnrate1h"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[2h]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[2h]))
          )
          +
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[2h]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[2h]))
      labels:
        verb: write
      record: "apiserver_request:burnrate2h"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[30m]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[30m]))
          )
          +
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[30m]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[30m]))
      labels:
        verb: write
      record: "apiserver_request:burnrate30m"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[3d]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[3d]))
          )
          +
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[3d]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[3d]))
      labels:
        verb: write
      record: "apiserver_request:burnrate3d"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[5m]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[5m]))
          )
          +
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[5m]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[5m]))
      labels:
        verb: write
      record: "apiserver_request:burnrate5m"
    - expr: |
        (
          (
            # too slow
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[6h]))
            -
            sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[6h]))
          )
          +
          sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[6h]))
        )
        /
        sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[6h]))
      labels:
        verb: write
      record: "apiserver_request:burnrate6h"
    
  

spec.groups.kubernetes-apps.rules.KubeJobNotCompleted.expr  (monitoring.coreos.com/v1/PrometheusRule/monitoring/kube-prometheus-stack-kubernetes-apps)
  ± value change in multiline text (one insert, one deletion)
      time() - max by (namespace, job_name, cluster) (kube_job_status_start_time{job="kube-state-metrics", namespace=~".*"}
        and
    - kube_job_status_active{job="kube-state-metrics", namespace=~".*"} > 0) > 43200
    -   unless on (namespace, job_name, cluster)
    - max by (namespace, job_name, cluster) (kube_job_labels{job="kube-state-metrics", namespace=~".*",label_excluded_from_alerts="true"} == 1)
    + kube_job_status_active{job="kube-state-metrics", namespace=~".*"} > 0) > 43200
  
  

spec.groups.kubernetes-apps.rules.KubePdbNotEnoughHealthyPods.expr  (monitoring.coreos.com/v1/PrometheusRule/monitoring/kube-prometheus-stack-kubernetes-apps)
  ± value change in multiline text (no inserts, one deletion)
      (
        kube_poddisruptionbudget_status_desired_healthy{job="kube-state-metrics", namespace=~".*"}
        -
        kube_poddisruptionbudget_status_current_healthy{job="kube-state-metrics", namespace=~".*"}
      )
    - > 0
    - and
    - kube_poddisruptionbudget_status_expected_pods{job="kube-state-metrics", namespace=~".*"}
      > 0
  
  

spec.endpoints.0  (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-coredns)
  - one map entry removed:
    authorization:
      type: Bearer
      credentials:
        name: kube-prometheus-stack-prometheus-token
        key: token
  
  + one map entry added:
    bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
  

spec.endpoints.0  (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-apiserver)
  - one map entry removed:
    authorization:
      type: Bearer
      credentials:
        name: kube-prometheus-stack-prometheus-token
        key: token
  
  + one map entry added:
    bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
  

spec.endpoints.0.tlsConfig  (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-apiserver)
  - one map entry removed:
    ca:
      configMap:
        name: kube-root-ca.crt
        key: ca.crt
  
  + one map entry added:
    caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  

spec.endpoints.0  (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kube-controller-manager)
  - one map entry removed:
    authorization:
      type: Bearer
      credentials:
        name: kube-prometheus-stack-prometheus-token
        key: token
  
  + one map entry added:
    bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
  

spec.endpoints.0.tlsConfig  (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kube-controller-manager)
  - one map entry removed:
    ca:
      configMap:
        name: kube-root-ca.crt
        key: ca.crt
  
  + one map entry added:
    caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  

spec.endpoints.0  (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kube-etcd)
  - one map entry removed:
    authorization:
      type: Bearer
      credentials:
        name: kube-prometheus-stack-prometheus-token
        key: token
  
  + one map entry added:
    bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
  

spec.endpoints.0  (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kube-scheduler)
  - one map entry removed:
    authorization:
      type: Bearer
      credentials:
        name: kube-prometheus-stack-prometheus-token
        key: token
  
  + one map entry added:
    bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
  

spec.endpoints.0.tlsConfig  (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kube-scheduler)
  - one map entry removed:
    ca:
      configMap:
        name: kube-root-ca.crt
        key: ca.crt
  
  + one map entry added:
    caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  

spec.endpoints  (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kubelet)
  - three list entries removed:
    - authorization:
        type: Bearer
        credentials:
          name: kube-prometheus-stack-prometheus-token
          key: token
      honorLabels: true
      honorTimestamps: true
      metricRelabelings:
      - action: drop
        regex: (csi_operations|storage_operation_duration)_seconds_bucket;(0.25|2.5|15|25|120|600)(\.0)?
        sourceLabels:
        - __name__
        - le
      port: https-metrics
      relabelings:
      - action: replace
        sourceLabels:
        - __metrics_path__
        targetLabel: metrics_path
      scheme: https
      tlsConfig:
        ca:
          configMap:
            name: kube-root-ca.crt
            key: ca.crt
        insecureSkipVerify: true
    - authorization:
        type: Bearer
        credentials:
          name: kube-prometheus-stack-prometheus-token
          key: token
      honorLabels: true
      honorTimestamps: true
      interval: 10s
      metricRelabelings:
      - action: drop
        regex: container_cpu_(cfs_throttled_seconds_total|load_average_10s|system_seconds_total|user_seconds_total)
        sourceLabels:
        - __name__
      - action: drop
        regex: container_fs_(io_current|io_time_seconds_total|io_time_weighted_seconds_total|reads_merged_total|sector_reads_total|sector_writes_total|writes_merged_total)
        sourceLabels:
        - __name__
      - action: drop
        regex: container_memory_(mapped_file|swap)
        sourceLabels:
        - __name__
      - action: drop
        regex: container_(file_descriptors|tasks_state|threads_max)
        sourceLabels:
        - __name__
      - action: drop
        regex: container_memory_failures_total;hierarchy
        sourceLabels:
        - __name__
        - scope
      - action: drop
        regex: "container_network_.*;(cali|cilium|cni|lxc|nodelocaldns|tunl).*"
        sourceLabels:
        - __name__
        - interface
      - action: drop
        regex: "container_spec.*"
        sourceLabels:
        - __name__
      - action: drop
        regex: .+;
        sourceLabels:
        - id
        - pod
      path: /metrics/cadvisor
      port: https-metrics
      relabelings:
      - action: replace
        sourceLabels:
        - __metrics_path__
        targetLabel: metrics_path
      scheme: https
      tlsConfig:
        ca:
          configMap:
            name: kube-root-ca.crt
            key: ca.crt
        insecureSkipVerify: true
      trackTimestampsStaleness: true
    - authorization:
        type: Bearer
        credentials:
          name: kube-prometheus-stack-prometheus-token
          key: token
      honorLabels: true
      honorTimestamps: true
      path: /metrics/probes
      port: https-metrics
      relabelings:
      - action: replace
        sourceLabels:
        - __metrics_path__
        targetLabel: metrics_path
      scheme: https
      tlsConfig:
        ca:
          configMap:
            name: kube-root-ca.crt
            key: ca.crt
        insecureSkipVerify: true
    
  
  + three list entries added:
    - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
      honorLabels: true
      honorTimestamps: true
      metricRelabelings:
      - action: drop
        regex: (csi_operations|storage_operation_duration)_seconds_bucket;(0.25|2.5|15|25|120|600)(\.0)?
        sourceLabels:
        - __name__
        - le
      port: https-metrics
      relabelings:
      - action: replace
        sourceLabels:
        - __metrics_path__
        targetLabel: metrics_path
      scheme: https
      tlsConfig:
        caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        insecureSkipVerify: true
    - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
      honorLabels: true
      honorTimestamps: true
      interval: 10s
      metricRelabelings:
      - action: drop
        regex: container_cpu_(cfs_throttled_seconds_total|load_average_10s|system_seconds_total|user_seconds_total)
        sourceLabels:
        - __name__
      - action: drop
        regex: container_fs_(io_current|io_time_seconds_total|io_time_weighted_seconds_total|reads_merged_total|sector_reads_total|sector_writes_total|writes_merged_total)
        sourceLabels:
        - __name__
      - action: drop
        regex: container_memory_(mapped_file|swap)
        sourceLabels:
        - __name__
      - action: drop
        regex: container_(file_descriptors|tasks_state|threads_max)
        sourceLabels:
        - __name__
      - action: drop
        regex: container_memory_failures_total;hierarchy
        sourceLabels:
        - __name__
        - scope
      - action: drop
        regex: "container_network_.*;(cali|cilium|cni|lxc|nodelocaldns|tunl).*"
        sourceLabels:
        - __name__
        - interface
      - action: drop
        regex: "container_spec.*"
        sourceLabels:
        - __name__
      - action: drop
        regex: .+;
        sourceLabels:
        - id
        - pod
      path: /metrics/cadvisor
      port: https-metrics
      relabelings:
      - action: replace
        sourceLabels:
        - __metrics_path__
        targetLabel: metrics_path
      scheme: https
      tlsConfig:
        caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        insecureSkipVerify: true
      trackTimestampsStaleness: true
    - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
      honorLabels: true
      honorTimestamps: true
      path: /metrics/probes
      port: https-metrics
      relabelings:
      - action: replace
        sourceLabels:
        - __metrics_path__
        targetLabel: metrics_path
      scheme: https
      tlsConfig:
        caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
        insecureSkipVerify: true
    
  

spec.template.spec  (batch/v1/Job/monitoring/kube-prometheus-stack-admission-create)
  - one map entry removed:
    nodeSelector:
      kubernetes.io/os: linux

spec.template.spec.containers.create.image  (batch/v1/Job/monitoring/kube-prometheus-stack-admission-create)
  ± value change
    - ghcr.io/jkroepke/kube-webhook-certgen:1.8.9
    + ghcr.io/jkroepke/kube-webhook-certgen:1.8.4

spec.template.spec  (batch/v1/Job/monitoring/kube-prometheus-stack-admission-patch)
  - one map entry removed:
    nodeSelector:
      kubernetes.io/os: linux

spec.template.spec.containers.patch.image  (batch/v1/Job/monitoring/kube-prometheus-stack-admission-patch)
  ± value change
    - ghcr.io/jkroepke/kube-webhook-certgen:1.8.9
    + ghcr.io/jkroepke/kube-webhook-certgen:1.8.4

Pipeline

## flate diff ``` rules (rbac.authorization.k8s.io/v1/ClusterRole/kube-prometheus-stack-operator) - six list entries removed: + three list entries added: - resources: - resources: - alertmanagers - alertmanagers - prometheusagents - alertmanagers/finalizers - prometheuses - alertmanagers/status - thanosrulers - alertmanagerconfigs apiGroups: - prometheuses - monitoring.coreos.com - prometheuses/finalizers verbs: - prometheuses/status - patch - prometheusagents - resources: - prometheusagents/finalizers - alertmanagers/status - prometheusagents/status - podmonitors/status - thanosrulers - probes/status - thanosrulers/finalizers - prometheuses/status - thanosrulers/status - prometheusagents/status - scrapeconfigs - prometheusrules/status - scrapeconfigs/status - scrapeconfigs/status - servicemonitors - servicemonitors/status - servicemonitors/status - thanosrulers/status - podmonitors apiGroups: - podmonitors/status - monitoring.coreos.com - probes verbs: - probes/status - create - prometheusrules - update - prometheusrules/status - patch apiGroups: - delete - monitoring.coreos.com - resources: verbs: - alertmanagers/finalizers - "*" - prometheusagents/finalizers - resources: - prometheuses/finalizers - statefulsets - thanosrulers/finalizers apiGroups: apiGroups: - apps - monitoring.coreos.com verbs: verbs: - "*" - update - resources: - resources: - configmaps - alertmanagers - secrets - alertmanagerconfigs apiGroups: - podmonitors - - probes verbs: - prometheusagents - "*" - prometheuses - prometheusrules - servicemonitors - scrapeconfigs - thanosrulers apiGroups: - monitoring.coreos.com verbs: - get - list - watch - resources: - statefulsets apiGroups: - apps verbs: - get - list - watch - create - update - patch - delete - resources: - configmaps - secrets apiGroups: - verbs: - get - list - watch - create - update - delete metadata (v1/Service/kube-privileged/node-exporter) + one map entry added: annotations: prometheus.io/scrape: "true" spec.template.spec.containers.kube-state-metrics.image (apps/v1/Deployment/monitoring/kube-state-metrics) ± value change - registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.20.0 + registry.k8s.io/kube-state-metrics/kube-state-metrics:v2.19.1 spec.template.spec (apps/v1/Deployment/monitoring/kube-prometheus-stack-operator) - one map entry removed: nodeSelector: kubernetes.io/os: linux spec.template.spec.containers.kube-prometheus-stack.args (apps/v1/Deployment/monitoring/kube-prometheus-stack-operator) - three list entries removed: - "--prometheus-config-reloader=quay.io/prometheus-operator/prometheus-config-reloader:v0.94.1" - "--thanos-default-base-image=quay.io/thanos/thanos:v0.42.4" - "--secret-field-selector=type!=kubernetes.io/dockercfg,type!=helm.sh/release.v1" + three list entries added: - "--prometheus-config-reloader=quay.io/prometheus-operator/prometheus-config-reloader:v0.92.1" - "--thanos-default-base-image=quay.io/thanos/thanos:v0.42.2" - "--secret-field-selector=type!=kubernetes.io/dockercfg,type!=kubernetes.io/service-account-token,type!=helm.sh/release.v1" spec.template.spec.containers.kube-prometheus-stack.image (apps/v1/Deployment/monitoring/kube-prometheus-stack-operator) ± value change - quay.io/prometheus-operator/prometheus-operator:v0.94.1 + quay.io/prometheus-operator/prometheus-operator:v0.92.1 spec (monitoring.coreos.com/v1/Alertmanager/monitoring/kube-prometheus-stack) - one map entry removed: + one map entry added: nodeSelector: hostNetwork: false kubernetes.io/os: linux spec.image (monitoring.coreos.com/v1/Alertmanager/monitoring/kube-prometheus-stack) ± value change - quay.io/prometheus/alertmanager:v0.34.1 + quay.io/prometheus/alertmanager:v0.33.1 spec.version (monitoring.coreos.com/v1/Alertmanager/monitoring/kube-prometheus-stack) ± value change - v0.34.1 + v0.33.1 spec (monitoring.coreos.com/v1/Prometheus/monitoring/kube-prometheus-stack) - one map entry removed: nodeSelector: kubernetes.io/os: linux spec.image (monitoring.coreos.com/v1/Prometheus/monitoring/kube-prometheus-stack) ± value change - quay.io/prometheus/prometheus:v3.15.0-distroless + quay.io/prometheus/prometheus:v3.13.1-distroless spec.version (monitoring.coreos.com/v1/Prometheus/monitoring/kube-prometheus-stack) ± value change - v3.15.0-distroless + v3.13.1-distroless spec.groups.kube-apiserver-burnrate.rules.rules (monitoring.coreos.com/v1/PrometheusRule/monitoring/kube-prometheus-stack-kube-apiserver-burnrate.rules) - eleven list entries removed: - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward"}[5m])) - ( ( sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope=~"resource|",le=~"1(\\.0)?"}[5m])) or vector(0) ) + sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="namespace",le=~"5(\\.0)?"}[5m])) + sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="cluster",le=~"30(\\.0)?"}[5m])) ) ) + # errors sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET",code=~"5.."}[5m])) ) labels: verb: read record: "cluster_verb:apiserver_request_sli_bad_events:rate5m" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[5m])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[5m])) ) + # errors sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[5m])) ) labels: verb: write record: "cluster_verb:apiserver_request_sli_bad_events:rate5m" - expr: "sum by (cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"LIST|GET\"}[5m]))" labels: verb: read record: "cluster_verb:apiserver_request_sli_events:rate5m" - expr: "sum by (cluster) (rate(apiserver_request_total{job=\"apiserver\",verb=~\"POST|PUT|PATCH|DELETE\"}[5m]))" labels: verb: write record: "cluster_verb:apiserver_request_sli_events:rate5m" - expr: | cluster_verb:apiserver_request_sli_bad_events:rate5m / cluster_verb:apiserver_request_sli_events:rate5m record: "apiserver_request:burnrate5m" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[1h])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[1h])) ) + # errors sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[1h])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[1h])) labels: verb: write record: "apiserver_request:burnrate1h" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[2h])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[2h])) ) + # errors sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[2h])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[2h])) labels: verb: write record: "apiserver_request:burnrate2h" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[30m])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[30m])) ) + # errors sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[30m])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[30m])) labels: verb: write record: "apiserver_request:burnrate30m" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[6h])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[6h])) ) + # errors sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[6h])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[6h])) labels: verb: write record: "apiserver_request:burnrate6h" - expr: | avg_over_time(cluster_verb:apiserver_request_sli_bad_events:rate5m[1d]) / avg_over_time(cluster_verb:apiserver_request_sli_events:rate5m[1d]) record: "apiserver_request:burnrate1d" - expr: | avg_over_time(cluster_verb:apiserver_request_sli_bad_events:rate5m[3d]) / avg_over_time(cluster_verb:apiserver_request_sli_events:rate5m[3d]) record: "apiserver_request:burnrate3d" + ten list entries added: - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward"}[1d])) - ( ( sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope=~"resource|",le=~"1(\\.0)?"}[1d])) or vector(0) ) + sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="namespace",le=~"5(\\.0)?"}[1d])) + sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="cluster",le=~"30(\\.0)?"}[1d])) ) ) + # errors sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET",code=~"5.."}[1d])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET"}[1d])) labels: verb: read record: "apiserver_request:burnrate1d" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward"}[3d])) - ( ( sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope=~"resource|",le=~"1(\\.0)?"}[3d])) or vector(0) ) + sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="namespace",le=~"5(\\.0)?"}[3d])) + sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="cluster",le=~"30(\\.0)?"}[3d])) ) ) + # errors sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET",code=~"5.."}[3d])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET"}[3d])) labels: verb: read record: "apiserver_request:burnrate3d" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward"}[5m])) - ( ( sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope=~"resource|",le=~"1(\\.0)?"}[5m])) or vector(0) ) + sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="namespace",le=~"5(\\.0)?"}[5m])) + sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"LIST|GET",subresource!~"proxy|attach|log|exec|portforward",scope="cluster",le=~"30(\\.0)?"}[5m])) ) ) + # errors sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET",code=~"5.."}[5m])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"LIST|GET"}[5m])) labels: verb: read record: "apiserver_request:burnrate5m" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[1d])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[1d])) ) + sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[1d])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[1d])) labels: verb: write record: "apiserver_request:burnrate1d" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[1h])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[1h])) ) + sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[1h])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[1h])) labels: verb: write record: "apiserver_request:burnrate1h" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[2h])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[2h])) ) + sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[2h])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[2h])) labels: verb: write record: "apiserver_request:burnrate2h" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[30m])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[30m])) ) + sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[30m])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[30m])) labels: verb: write record: "apiserver_request:burnrate30m" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[3d])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[3d])) ) + sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[3d])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[3d])) labels: verb: write record: "apiserver_request:burnrate3d" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[5m])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[5m])) ) + sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[5m])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[5m])) labels: verb: write record: "apiserver_request:burnrate5m" - expr: | ( ( # too slow sum by (cluster) (rate(apiserver_request_sli_duration_seconds_count{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward"}[6h])) - sum by (cluster) (rate(apiserver_request_sli_duration_seconds_bucket{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",subresource!~"proxy|attach|log|exec|portforward",le=~"1(\\.0)?"}[6h])) ) + sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE",code=~"5.."}[6h])) ) / sum by (cluster) (rate(apiserver_request_total{job="apiserver",verb=~"POST|PUT|PATCH|DELETE"}[6h])) labels: verb: write record: "apiserver_request:burnrate6h" spec.groups.kubernetes-apps.rules.KubeJobNotCompleted.expr (monitoring.coreos.com/v1/PrometheusRule/monitoring/kube-prometheus-stack-kubernetes-apps) ± value change in multiline text (one insert, one deletion) time() - max by (namespace, job_name, cluster) (kube_job_status_start_time{job="kube-state-metrics", namespace=~".*"} and - kube_job_status_active{job="kube-state-metrics", namespace=~".*"} > 0) > 43200 - unless on (namespace, job_name, cluster) - max by (namespace, job_name, cluster) (kube_job_labels{job="kube-state-metrics", namespace=~".*",label_excluded_from_alerts="true"} == 1) + kube_job_status_active{job="kube-state-metrics", namespace=~".*"} > 0) > 43200 spec.groups.kubernetes-apps.rules.KubePdbNotEnoughHealthyPods.expr (monitoring.coreos.com/v1/PrometheusRule/monitoring/kube-prometheus-stack-kubernetes-apps) ± value change in multiline text (no inserts, one deletion) ( kube_poddisruptionbudget_status_desired_healthy{job="kube-state-metrics", namespace=~".*"} - kube_poddisruptionbudget_status_current_healthy{job="kube-state-metrics", namespace=~".*"} ) - > 0 - and - kube_poddisruptionbudget_status_expected_pods{job="kube-state-metrics", namespace=~".*"} > 0 spec.endpoints.0 (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-coredns) - one map entry removed: authorization: type: Bearer credentials: name: kube-prometheus-stack-prometheus-token key: token + one map entry added: bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token spec.endpoints.0 (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-apiserver) - one map entry removed: authorization: type: Bearer credentials: name: kube-prometheus-stack-prometheus-token key: token + one map entry added: bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token spec.endpoints.0.tlsConfig (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-apiserver) - one map entry removed: ca: configMap: name: kube-root-ca.crt key: ca.crt + one map entry added: caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt spec.endpoints.0 (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kube-controller-manager) - one map entry removed: authorization: type: Bearer credentials: name: kube-prometheus-stack-prometheus-token key: token + one map entry added: bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token spec.endpoints.0.tlsConfig (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kube-controller-manager) - one map entry removed: ca: configMap: name: kube-root-ca.crt key: ca.crt + one map entry added: caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt spec.endpoints.0 (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kube-etcd) - one map entry removed: authorization: type: Bearer credentials: name: kube-prometheus-stack-prometheus-token key: token + one map entry added: bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token spec.endpoints.0 (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kube-scheduler) - one map entry removed: authorization: type: Bearer credentials: name: kube-prometheus-stack-prometheus-token key: token + one map entry added: bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token spec.endpoints.0.tlsConfig (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kube-scheduler) - one map entry removed: ca: configMap: name: kube-root-ca.crt key: ca.crt + one map entry added: caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt spec.endpoints (monitoring.coreos.com/v1/ServiceMonitor/monitoring/kube-prometheus-stack-kubelet) - three list entries removed: - authorization: type: Bearer credentials: name: kube-prometheus-stack-prometheus-token key: token honorLabels: true honorTimestamps: true metricRelabelings: - action: drop regex: (csi_operations|storage_operation_duration)_seconds_bucket;(0.25|2.5|15|25|120|600)(\.0)? sourceLabels: - __name__ - le port: https-metrics relabelings: - action: replace sourceLabels: - __metrics_path__ targetLabel: metrics_path scheme: https tlsConfig: ca: configMap: name: kube-root-ca.crt key: ca.crt insecureSkipVerify: true - authorization: type: Bearer credentials: name: kube-prometheus-stack-prometheus-token key: token honorLabels: true honorTimestamps: true interval: 10s metricRelabelings: - action: drop regex: container_cpu_(cfs_throttled_seconds_total|load_average_10s|system_seconds_total|user_seconds_total) sourceLabels: - __name__ - action: drop regex: container_fs_(io_current|io_time_seconds_total|io_time_weighted_seconds_total|reads_merged_total|sector_reads_total|sector_writes_total|writes_merged_total) sourceLabels: - __name__ - action: drop regex: container_memory_(mapped_file|swap) sourceLabels: - __name__ - action: drop regex: container_(file_descriptors|tasks_state|threads_max) sourceLabels: - __name__ - action: drop regex: container_memory_failures_total;hierarchy sourceLabels: - __name__ - scope - action: drop regex: "container_network_.*;(cali|cilium|cni|lxc|nodelocaldns|tunl).*" sourceLabels: - __name__ - interface - action: drop regex: "container_spec.*" sourceLabels: - __name__ - action: drop regex: .+; sourceLabels: - id - pod path: /metrics/cadvisor port: https-metrics relabelings: - action: replace sourceLabels: - __metrics_path__ targetLabel: metrics_path scheme: https tlsConfig: ca: configMap: name: kube-root-ca.crt key: ca.crt insecureSkipVerify: true trackTimestampsStaleness: true - authorization: type: Bearer credentials: name: kube-prometheus-stack-prometheus-token key: token honorLabels: true honorTimestamps: true path: /metrics/probes port: https-metrics relabelings: - action: replace sourceLabels: - __metrics_path__ targetLabel: metrics_path scheme: https tlsConfig: ca: configMap: name: kube-root-ca.crt key: ca.crt insecureSkipVerify: true + three list entries added: - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token honorLabels: true honorTimestamps: true metricRelabelings: - action: drop regex: (csi_operations|storage_operation_duration)_seconds_bucket;(0.25|2.5|15|25|120|600)(\.0)? sourceLabels: - __name__ - le port: https-metrics relabelings: - action: replace sourceLabels: - __metrics_path__ targetLabel: metrics_path scheme: https tlsConfig: caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecureSkipVerify: true - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token honorLabels: true honorTimestamps: true interval: 10s metricRelabelings: - action: drop regex: container_cpu_(cfs_throttled_seconds_total|load_average_10s|system_seconds_total|user_seconds_total) sourceLabels: - __name__ - action: drop regex: container_fs_(io_current|io_time_seconds_total|io_time_weighted_seconds_total|reads_merged_total|sector_reads_total|sector_writes_total|writes_merged_total) sourceLabels: - __name__ - action: drop regex: container_memory_(mapped_file|swap) sourceLabels: - __name__ - action: drop regex: container_(file_descriptors|tasks_state|threads_max) sourceLabels: - __name__ - action: drop regex: container_memory_failures_total;hierarchy sourceLabels: - __name__ - scope - action: drop regex: "container_network_.*;(cali|cilium|cni|lxc|nodelocaldns|tunl).*" sourceLabels: - __name__ - interface - action: drop regex: "container_spec.*" sourceLabels: - __name__ - action: drop regex: .+; sourceLabels: - id - pod path: /metrics/cadvisor port: https-metrics relabelings: - action: replace sourceLabels: - __metrics_path__ targetLabel: metrics_path scheme: https tlsConfig: caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecureSkipVerify: true trackTimestampsStaleness: true - bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token honorLabels: true honorTimestamps: true path: /metrics/probes port: https-metrics relabelings: - action: replace sourceLabels: - __metrics_path__ targetLabel: metrics_path scheme: https tlsConfig: caFile: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecureSkipVerify: true spec.template.spec (batch/v1/Job/monitoring/kube-prometheus-stack-admission-create) - one map entry removed: nodeSelector: kubernetes.io/os: linux spec.template.spec.containers.create.image (batch/v1/Job/monitoring/kube-prometheus-stack-admission-create) ± value change - ghcr.io/jkroepke/kube-webhook-certgen:1.8.9 + ghcr.io/jkroepke/kube-webhook-certgen:1.8.4 spec.template.spec (batch/v1/Job/monitoring/kube-prometheus-stack-admission-patch) - one map entry removed: nodeSelector: kubernetes.io/os: linux spec.template.spec.containers.patch.image (batch/v1/Job/monitoring/kube-prometheus-stack-admission-patch) ± value change - ghcr.io/jkroepke/kube-webhook-certgen:1.8.9 + ghcr.io/jkroepke/kube-webhook-certgen:1.8.4 ``` [Pipeline](https://ci.aether.earth/repos/4/pipeline/513)
renovate-bot force-pushed renovate/ghcr.io-rommapp-romm-5.x from a1cd32f9ab
All checks were successful
ci/crow/push/dns-test Pipeline was successful
ci/crow/pr/dns-test Pipeline was successful
ci/crow/push/tanka-test Pipeline was successful
ci/crow/pr/tanka-test Pipeline was successful
ci/crow/pr/tanka-diff Pipeline was successful
to a0b32e275d
Some checks failed
ci/crow/push/dns-test Pipeline was successful
ci/crow/pr/dns-test Pipeline failed
ci/crow/push/tanka-test Pipeline was successful
ci/crow/pr/tanka-test Pipeline was successful
ci/crow/pr/tanka-diff Pipeline was successful
2026-10-22 23:13:11 +00:00
Compare
Member

flate diff

No differences detected.

Pipeline

## flate diff No differences detected. [Pipeline](https://ci.aether.earth/repos/4/pipeline/710)
Some checks failed
ci/crow/push/dns-test Pipeline was successful
ci/crow/pr/tanka-test Pipeline was successful
ci/crow/pr/dns-test Pipeline failed
ci/crow/push/tanka-test Pipeline was successful
ci/crow/pr/tanka-diff Pipeline was successful
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin renovate/ghcr.io-rommapp-romm-5.x:renovate/ghcr.io-rommapp-romm-5.x
git switch renovate/ghcr.io-rommapp-romm-5.x

Merge

Merge the changes and update on Forgejo.
git switch main
git merge --no-ff renovate/ghcr.io-rommapp-romm-5.x
git switch renovate/ghcr.io-rommapp-romm-5.x
git rebase main
git switch main
git merge --ff-only renovate/ghcr.io-rommapp-romm-5.x
git switch renovate/ghcr.io-rommapp-romm-5.x
git rebase main
git switch main
git merge --no-ff renovate/ghcr.io-rommapp-romm-5.x
git switch main
git merge --squash renovate/ghcr.io-rommapp-romm-5.x
git switch main
git merge --ff-only renovate/ghcr.io-rommapp-romm-5.x
git switch main
git merge renovate/ghcr.io-rommapp-romm-5.x
git push origin main
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
ops/homelab!91
No description provided.