github liketrek/TREK v4.2.1

2 hours ago

Upvote your favourite feature requests

Fifteen reported issues off the 4.2.0 tag, two privately reported security advisories, and one operator request. No breaking changes and no schema change. This release appends no migrations at all, so a rollback to 4.2.0 stays possible on the data side. Two changes need a word before you update.

Read this first

  • Installed PWAs reload themselves once, and offline map tiles have to be downloaded again. The offline fix in this release replaces the old "wipe everything on a version change" behaviour with a proper worker update, but the code that runs on your device right now is still the old one, so it does its clean-up one last time on the first start after this update. Open TREK with a connection and leave it open until it has reloaded itself (about 23 MB). Afterwards, go to Settings → Offline and press Download for offline use once. Your trips, files and anything waiting to sync are stored separately and are not affected. Do not uninstall and reinstall the app to speed this up: that would also delete the trips you have stored offline. From the next update onwards this no longer happens.
  • Balances change on trips that have an expense nobody paid for. Such an expense used to be charged to the people it was split between even though no one was owed anything, which pulled the balances away from zero. It no longer counts. On a trip where you already settled up against one of those phantom debts, the balances will now read the other way round and the settle-up box may suggest new transfers between people who never shared a bill. Nothing is corrected automatically. Undo those entries in the settle-up history, then settle again from the corrected figures. Trips without a payer-less expense are unaffected.

Security

Both reported privately by @skeletonsec, both fixed in #2263.

  • The ntfy token an operator configures no longer reaches a server the user chose (GHSA-7pqc-fj3c-9346). The token fell back to the operator's whenever a request carried none, so any signed-in user could point ntfy at a listener they control, leave the token field empty, and be handed the operator's push token. That applied to the connection test and to every ordinary notification, since the ntfy server is a per-user setting. The operator's token now travels only to the operator's own server, which keeps every legitimate setup working, including the common one where the operator provides the server and users only set a topic.
  • Outgoing notification requests are re-checked at every redirect (GHSA-8mw6-xphx-886m). Webhook and ntfy deliveries validated only the address they were given and then followed redirects blindly, so a redirect pointing at a loopback, internal or cloud-metadata address was dialled by the server. Every hop is now checked and pinned individually. The same weakness sat in the shared helper behind photo, AirTrail and image requests, where a redirect could have returned an internal service's response to the caller rather than merely reaching it; that is fixed too. Credentials are also dropped when a redirect crosses to another host, the way browsers do.

Fixes

Sharing and journeys

  • Public journey share pages load again. A share with the map enabled and at least one geotagged photo went straight to the error screen from 1024px up: the sidebar map places its photo thumbnails in screen space, and it drew before the map had been given a centre and a zoom, which Leaflet answers by throwing. The photos now wait for the opening view and appear as soon as it lands (#2254, reported and fixed by @xvilo).
  • Anonymous visitors no longer trigger a failed authenticated request on every share page. System notices, background tasks and save-to-collection were only held back on the login and password routes, so a visitor to a shared trip or journey mounted all three and logged a 401 nobody could act on (@xvilo).
  • The language picker on a public journey page reaches all 23 languages. It used to be cut off at the bottom edge of the page hero, leaving most of the list unpickable; it now floats above the page and scrolls when the screen is short. On both share pages, trip and journey, it also closes on Escape or a click outside, which it never did, and Traditional Chinese stops labelling itself 简体中文 (#2248).

Planner and maps

  • The Plan tab fits a tablet, a foldable and a narrow window again. Anything between the phone breakpoint and 1024px, which is an iPad held upright as much as an unfolded foldable or a browser window at half a laptop's width, now opens one side panel at a time: the day plan first, the places list one tap away, instead of two panels squeezing the map into a 200px strip. The map opens framed on the trip rather than on half a continent, the floating map controls no longer sit on top of the panel tabs and the Add Place button and swallow their taps, and those tabs finally carry a name a screen reader can announce (#2247).
  • The phone map has a way back to the whole trip. Picking a day on the map narrows it to that day, and until now nothing took that filter off again: the chip rail can only swap one day for another, and a second tap on the active chip opens the day sheet. A small control now sits beside the rail, in map view only: it clears the day and frames the whole trip, and pressing it again returns to the day it came from, route and all (#2257).
  • The start and end date no longer sit on top of each other in the trip dialog. Both fields are in a two-column grid, and neither column could shrink below the width of its contents, so a long date plus the keyboard button beside it pushed each cell past its half. On a phone the end date came to rest over the start date with its own button clipped off the card edge. The date now shortens with an ellipsis inside its column instead (#2267).
  • A note in a day plan no longer covers its own up and down buttons with a pencil and a bin. Tapping the note opens it for editing, the same gesture the phone already used, and delete moved into that dialog. That also makes reordering a note clickable for the first time, on a mouse as well as on a touchscreen (#2249).
  • Arabic, Hebrew, Persian and Urdu place names on the vector basemaps render joined and in the right order. The map engines need an optional plugin for bidirectional text and Arabic contextual shaping, and neither loads it on its own, so labels came out as isolated letters in reverse. Raster basemaps hid this because their labels are drawn server-side, which is why it surfaced when the default basemap became a vector style (#2235).
  • Notes on the phone show and keep their website. The field can be entered, edited and cleared, a saved link opens from the note, and members without edit rights see it too, on phone and desktop (#2222).

Bookings and import

  • Saving a hotel booking stops leaving a duplicate place behind. The stay is linked to the place created for it, so opening the same hotel again and saving reuses that place instead of minting another unlinked copy every time (#2243).
  • Editing a booking no longer wipes the price shown on its card, whatever the booking type. The price survives an ordinary edit, an AirTrail sync and any API or MCP update, whether it came from a linked expense or was written onto the booking by the import, so it holds on an instance with the Costs addon switched off too. It is removed when the expense behind it is deleted rather than lingering with nothing behind it, and setting or clearing the price explicitly still works as sent (#2233).
  • AI booking import works with OpenAI reasoning models. Every request pinned a temperature of 0, which the gpt-5 family rejects outright, so the extraction failed before it began and burned a second doomed retry on the way. The parameter is now dropped only once a provider says it will not take it, so deterministic extraction stays the default everywhere else, local NuExtract included (#2262).
  • Booking import says why it found nothing. The bundled KDE Itinerary extractor's version now appears in the startup log and in a new admin-only GET /api/admin/system-info, and LOG_LEVEL=debug passes the extractor's own error lines through instead of filtering them away. A provider script missing from an older extractor used to look exactly like a provider TREK does not support (#2261).
  • How long one AI parsing call may take is now a single setting, LLM_TIMEOUT_MS, instead of three constants that disagreed and none of which was the ceiling that actually applied. The underlying HTTP client is held to the same value, so raising the ceiling now works at all: it used to cap out at five minutes of its own accord, which meant a larger number changed nothing and the call failed with a header timeout instead. The default is 15 minutes so heavier documents fit without a code change, and it can be lowered to fail fast on a cloud provider (#2230).

Costs

  • An expense with "No one paid yet" no longer creates debt owed to the payer of a different expense. It counts toward the trip total and appears in the Outstanding amount card, the balances add up to zero again, and settle-up stops offering payments between people who never shared a bill (#2225). See Read this first above for what this means on a trip you have already settled.
  • "Your share" no longer counts expenses nobody has paid for. The balances below it stopped counting them, so the two halves of the costs panel disagreed with each other.

Offline

  • Offline mode works again. A version bump no longer wipes the app shell and every downloaded map tile, so an installed PWA still starts without a connection; a session that launched offline can sync once the signal returns instead of staying dead for its whole life; and switching a finished trip on in the offline settings actually stores it rather than leaving the cache at zero (#2228).
  • An offline map download no longer risks the cache it is filling. Where the browser refuses persistent storage, which is Safari and anyone who declined the prompt, a trip's tiles now stop at zoom 12 instead of 16. Street level detail is missing offline, but the origin stops claiming tens of gigabytes of padded quota for a single trip, which is what made the browser evict the whole bucket, precached app shell included, and leave the installed app unable to start at all (#2228).
  • The offline settings screen tells the truth: a download that never started, could not finish, or had nothing to store says so, instead of painting a full progress bar and a green "Ready for offline use" over a run of nothing, and a finished one says how many trips it stored. The list stops claiming that finished trips are already on the device, where every one of them showed its switch on and read "Stored offline" while the download passed over it. A storage error is reported rather than shown as "no trips cached", and Clear cache is no longer greyed out over a store that a half-finished download left behind (#2228).

Lists

  • Creating a list with a cover image uploaded from your own device keeps the image, and the new list opens once it is created. The id of the new list was read out of a field the response does not carry, so it never arrived: the upload was skipped without a word, and nothing was opened or selected afterwards. A cover picked from the photo search was never affected.

Integrations and admin

  • MCP OAuth: clients registering a private-use redirect scheme such as workbuddy:// can connect. http://[::1] is accepted alongside the other loopback addresses, and a native client that registered a placeholder port may authorize on the port the OS handed it, per RFC 8252. The settings form and dynamic client registration now apply the same rules, which also closes a hole where a scheme like javascript: was accepted as long as its host read localhost. Clients you have already registered are not re-validated, so nothing that works today stops working (#2227).
  • Plugins whose declared trek range does not admit the running version, whether it asks for an older one, a newer one, or a range no version can satisfy, and plugins that never said which TREK versions they support, can be installed, updated and activated when the operator sets TREK_PLUGINS_IGNORE_TREK_RANGE. Every gate warns instead of refusing, the admin is told at each step, and neither the plugin-API version gate nor the Discover compatibility verdict is loosened. While it is on, an entry Discover flags as incompatible no longer offers the newest release this TREK can run: the button becomes Install anyway and takes the newest published version, after the warning (#2226). Unset, nothing about plugin installation changes.
  • While TREK_PLUGINS_IGNORE_TREK_RANGE is set, the plugins panel says so on its own: a Version checks off badge sits in the header, on the desktop admin and on the phone, for as long as the variable is set. A second admin who did not set it can see that the version gate is loosened without installing anything.
  • A plugin that only got in because the version check was off stays marked for as long as it runs. Its row carries a chip reading "Outside its TREK range, version checks off", or "Declares no TREK range, version checks off" for one that declared none, so the warning outlives the install dialog and is still there weeks later.

Schema

No migrations. The schema is byte for byte the one 4.2.0 shipped, so this update writes nothing to the database structure and a rollback stays possible on the data side.

Full changelog: v4.2.0...v4.2.1

Don't miss a new TREK release

NewReleases is sending notifications on new releases.