Skip to content

Releases: liketrek/TREK

v3.4.0

Choose a tag to compare

@mauriceboe mauriceboe released this 18 Jul 21:01
3ca1ef3
Release 3 4 0 (1)

The plugin platform matures, and Costs goes properly multi-currency. A plugin can now deliver its own notifications (Gotify, Pushover, Telegram…), declare the exact TREK versions it runs on, carry a signature TREK verifies and badges, ship an icon and its own settings page — and trek-plugin becomes a guided path from scaffold to published. Costs gets an editable trip currency that re-anchors the whole budget, per-currency settle-up payments, and totals, place prices and the PDF that stop pretending everything is EUR. Plus AirTrail multi-leg imports, coordinate-first Atlas, an offline Help centre that ships with the build, Catalan as language 23, and a broad reliability, mobile and security pass.

No breaking changes — every new capability is opt-in, and all migrations run automatically on startup.

The team is growing — and TREK has a new home

Two things beyond the changelog:

  • A new maintainer. @subdee (Konstantinos) joins @jubnl and me steering TREK — and he's already all over this release. And @fbnlrz (Fabi), who builds the plugin-dev skill, is stepping in to help with community management. Thank you both. 🙌
  • TREK now lives in its own GitHub organisation: github.com/liketrek. The repo is now liketrek/TREK — existing links and clones keep redirecting, so nothing breaks, but that's the new home.

Features

  • A plugin can register a notification channel — the four built-in channels (in-app, email, webhook, ntfy) were a closed set, so a plugin could produce a notification but never deliver one. A plugin now declares hooks.notificationChannel + hook:notification-channel; email/webhook/ntfy become providers behind a channel registry, and the hook runs userless (the host passes the recipient's decrypted settings in as config), so a channel plugin gets a push token without gaining the right to read that user's trips.
  • AirTrail connecting flights import as one multi-leg booking — layovers were inexpressible (every flight came in as its own reservation, and the connection country counted as visited in Atlas). The picker joins chains by airport continuity within 24h, and a joined booking imports detached from sync, since AirTrail has no multi-leg entity to round-trip to. Closes #1535
  • A plugin declares the TREK versions it supports, and the host enforces it — the manifest's trek is now a semver range, not a lower bound that cannot express "stops working at 4.0". install/host-compat.ts is the single answer for install, sideload, dev-link and activation; prereleases coerce to their target release, and an unparseable APP_VERSION warns rather than blocking. Discover badges an incompatible entry and offers the newest version that does fit.
  • Plugin signature status, and a scoped re-trust override — signatures were verified and TOFU-pinned but never surfaced: an unsigned plugin looked identical to a signed one, and a refused update died in a toast. Refusals now carry codes, persist on the plugin row, and are badged. Only SIGNATURE_KEY_CHANGED is overridable (keys rotate; bad bytes don't), re-trust re-pins and updates in one call, the artifact must still verify under the new key, and both fingerprints go to the audit log.
  • A plugin can declare an icon — a lucide icon name in the manifest, resolved in one place (PluginIcon.tsx) and rendered everywhere a plugin appears, instead of a generic Blocks tile everywhere. Validation warns rather than errors, since the SDK's icon-name snapshot can legitimately trail the host.
  • The plugin CLIs warn when the SDK is out of date — the entry format, validator and permission catalog move with the host, so a stale SDK can pack and submit something today's registry CI rejects. Backed by update-notifier, so it never blocks on the network, and it stays silent in CI and off a TTY.
  • rawBodyBase64 on PluginRequest — a webhook author needs the exact bytes the sender signed; re-serializing the parsed body will not reproduce them. The host already set it on auth:false routes; it's now part of the public type.
  • The SDK mock host can drive settings actions and notification channelshook() alone can't express them, so a plugin could pass its tests and still break in production. app.action() models a button as user-initiated, app.channel.send/test models the channel as userless with config passed in.
  • The trip currency is editable, and changing it re-anchors the budget — the trip currency is the base every expense and settle-up is netted against, but the only picker for it lived in the legacy Budget addon panel, so on the Costs panel a trip was stuck with whatever it was created as. It's now a field on the trip form (create and edit, gated on trip_edit); the REST and MCP write paths already accepted it. Changing it isn't a rename, though: a frozen exchange_rate is "units of the row's currency per 1 trip currency" and currency = NULL means "the trip's own", so both are relative to the outgoing base. rebaseTripCurrency() runs first, pinning the implicit rows to the outgoing currency and re-freezing every rate against the incoming one — no stored amount is rewritten, and each expense keeps its real-world value across the switch.
  • Settle-up payments carry their own currency — paying a rouble debt in euros is normal, and the server has stored a currency + frozen exchange_rate on every transfer since #1445, but the UI never let anyone choose one, so a payment silently inherited whatever the viewer's display currency happened to be. The payment modal now has a picker (and reopens a payment in the currency it was recorded in), and a foreign payment shows in the ledger as $30.00 → 27,00 € like a foreign expense does, instead of stamping the display currency's symbol onto the raw stored number.
  • The display currency is optional, falling back to the trip's — Costs already resolved default_currency || trip.currency, but the setting could never actually be empty: the store seeded 'USD', so a user who had never touched it silently had every trip converted into dollars. The picker now leads with a Trip currency option, so an unset preference means "show each trip in its own currency" instead of forcing them all through one code. An explicit empty value persists and beats the admin-set instance default — it's a deliberate choice, not an absence. This also brings the public share's fallback to life: the payload has resolved sharer's currency → trip currency since #1361, but that branch was unreachable while every owner had a currency forced on them.
  • The in-app Help pages are served from the wiki that ships with the build/help fetched its markdown from raw.githubusercontent.com at runtime, so a self-hosted install was shown docs from main rather than the version it was actually running, and help broke without network access. It now reads the repo's wiki/ from disk, with no copy or build step — one __dirname anchor resolves in dev, a source install and Docker alike. GitHub survives only as a fallback for when that directory can't be found: a page missing from a present wiki is a genuine 404, since falling back per-file would reintroduce the version skew this removes. Shipping it took two Docker edits, not one — .dockerignore excluded wiki/ outright, so the COPY alone would have produced an image with no wiki, quietly masked by the fallback. TREK_WIKI_DIR is an off-by-default escape hatch for serving your own docs. Costs ~30 MB of image size.
  • Booking routes show per trip, with an account default and a bulk toggle — a new always show booking routes setting seeds any trip whose routes were never touched, and a show-all/hide-all button in the day-plan toolbar flips a trip explicitly. The per-leg toggle still overrides in either mode.
  • A plugin can ship its own settings pagecapabilities.settingsUi: true frames the plugin's settings.html as a card under Settings → Plugins, in the same sandbox and postMessage bridge as its widget. Hosts that predate the flag strip it at install, so old instances simply don't show the card.
  • Maps open framed on their places — a trip in Japan opened on a world view at 0,0 and then flew across the planet; each renderer now derives its opening camera from its places at construction. The home-city default centre/zoom settings are dropped, since nothing reads them anymore.
  • MCP exposes public transit planning tools — search and journey creation in parity with the web app, accepting every mode the provider can actually return (the old whitelist silently dropped itineraries with e.g. an AIRPLANE leg) and reporting a dropped count instead of conflating bad provider data with "no routes exist".
  • A visited Atlas region can be hidden, cascading to the country — a new hidden_regions tombstone mirrors hidden_countries; hiding a country's last visible region hides the country too, and re-marking clears both.
  • Catalan (ca) is a supported language — the full locale, at strict key parity with en.

Fixes

  • Every plain-HTTP request a plugin made was refused — Node normalises net.connect() args into an array, which undici's HTTP (not TLS) connector passes through; classifyConnect read host off it, got undefined, and fell back to localhost, so a declared public host was rejected as "not in the plugin's declared hosts". Failed closed, so never a hole, and unnoticed only because the one shipped egress plugin uses HTTPS.
  • TREK_PLUGIN_ALLOW_PRIVATE_EGRESS could never have worked — the guard reads it inside the plugin child, whose env the supe...
Read more

v3.3.0 — 10,000 stars 🌟

Choose a tag to compare

@mauriceboe mauriceboe released this 11 Jul 20:38
f9c992e

TREK just crossed 10,000 stars on GitHub, and it's honestly still surreal to write that. Thank you — to everyone who starred the repo, opened an issue, shipped a plugin, translated a string or sent a PR: this is yours as much as mine. TREK isn't just growing, it's accelerating, and the goal has never changed: not just the best open-source trip planner, but the best trip planner, full stop. We're closing that gap, fast. Onwards ❤️


📣 We're looking for another maintainer

TREK is growing faster than @jubnl and I can carry on our own. At this pace the workload just isn't sustainable for two people anymore. So we're looking for one more maintainer to help steer the project.

You'd be a great fit if you:

  • Know TREK's architecture well — or can get deep into it fast.
  • Have built your own projects before — ideally software of this kind, maybe even professionally.
  • Can write and understand code independently. AI assistants (Claude Code and the like) are absolutely welcome — but you genuinely need to understand the code you ship, not just paste it.

Interested? Email report@liketrek.com or reach Maurice on our Discord. We'd love to hear from you. 🙌


Plugins go from "can look at TREK and write a little" to "can automate TREK and connect it to the outside world" — full read/write symmetry across the planner, host-brokered notifications, AI, OAuth and inbound webhooks, plugin-provided notification channels (Gotify, Pushover, Telegram…), host-rendered hooks in tables, map, PDF, atlas and journal, and every call visible in a per-plugin activity log. Plus fixes across costs, calendars, transit, journeys and the planner. No breaking changes — every new capability is opt-in and admin-consented; existing plugins keep working.

Plugins: 3.2.0 vs 3.2.1 vs 3.3.0

Please consult the wiki for more information. Check out the “trek-plugin-dev” by @fbnlrz. It provides an AI agent with the entire SDK, the authorization model, the registry, and the one-click publishing workflow, so you can go from an idea to a published plugin in a single conversation.

Capability 3.2.0 3.2.1 3.3.0
Usable permissions ~8 ~18 55 (+ http:outbound:<host> wildcard)
Write / mutate methods 1 (costs.create) ~16 62 — + reservations, accommodations, packing + bags, files, collab, tags, todos, atlas, vacay, journal, collections, day notes, trips.create, …
Provider hooks 0 (declared but dead) 2 live 7 live — + tableContributor, mapMarkerProvider, pdfSectionProvider, atlasLayerProvider, journalEntryProvider
Read subsystems trips, costs, users + packing, files + collab, journal entries, atlas bucket list, file content, exchange rates — 17 read surfaces
Event subscriptions name + trip only + {entity, entityId} hint + grant-scoped entity snapshot
Entity metadata db:meta on trips/places/days + reservations, accommodations
UI extension points slots sidebar/hero, pages + trip-page type, + place-detail slot + day-detail/reservation-detail slots, core-tab replacement, tableContributor in 8 views, map-marker/PDF-section/atlas-layer/journal-entry render hooks
Host-mediated brokers notify, AI, OAuth client, inbound webhooks, per-user settings, exchange rates
Notification channels 4 built-in, hardcoded 4 built-in, hardcoded plugin-registered — Gotify, Pushover, Telegram, … as a real column in Settings → Notifications
Distribution & DX registry install + sideload .zip, typed SDK, design kit + download counts, dev-link hot-reload, SDK 1.4.0 (motion, bridge), validator parity
Safety & ops sandbox + egress control, capability hardening + per-plugin RPC rate limit, lifecycle recovery, uninstall secret purge, user activity audit log

Plugin platform

  • Read symmetry: plugins can now read collab notes/polls/messages, journal entries, atlas visited + bucket list, vacay, day notes, collections, categories, tags, todos, the trip roster, weather and exchange rates — plus file content behind its own db:read:files:content scope; trips gains getDays/getReservations/getAccommodations and cross-trip reads
  • Write expansion to 62 methods: reservations, accommodations, packing incl. bags (broadcasts respect per-user privacy, #858), files (attach/link/update/soft-delete), collab content, tags, todos, atlas, vacay, journal entries, collections, day notes, gated member-add and trips.create — each still gated by the acting user's own permissions
  • 5 new provider hooks live: tableContributor (host-rendered columns and actions in 8 views — reservations, transports, places, day, costs, packing, files, todos), mapMarkerProvider (trip-map overlay), pdfSectionProvider, atlasLayerProvider, journalEntryProvider
  • New UI surface: day-detail and reservation-detail widget slots; trip-page plugins can replace core planner tabs and pick their position; page plugins show up in the mobile bottom nav
  • Events carry an {entity, entityId} hint and a grant-scoped entity snapshot — filtered per plugin at delivery, never for private packing items or deletes
  • Entity metadata (db:meta) extended to reservations and accommodations (5 entity types)
  • Host-mediated brokers, all new: notify.send, ai.complete/ai.extract (host-brokered LLM access), an OAuth client (oauth.getToken + callback flow), trusted inbound webhooks with a signature-header allowlist, a per-user settings form + ctx.settings, and rates.get
  • Background jobs: declared jobs now run on schedule (jobs:run)
  • Settings-page actions: a plugin can put buttons on its own settings page (Test connection, Sync now) — user-initiated, so they act as the person who clicked
  • Emoji hygiene: emojis in plugin-provided text that TREK renders in its own chrome (badges, table columns, trip warnings, map-marker and calendar labels, PDF sections, notifications) are stripped at the render boundary, so plugin output matches TREK's lucide icon language — plugins use the declarative icon field for glyphs; a plugin's own sandboxed /ui frame is untouched

Notification channels

  • A plugin can now register a notification channel (hook:notification-channel) — Gotify, Pushover, Telegram, anything that takes a message — alongside the built-in in-app / email / webhook / ntfy. It shows up as a real column in every user's Settings → Notifications matrix, each user supplies their own credentials on the plugin's settings page, and per-event opt-outs work exactly as they do for a built-in
  • The four hardcoded channels became a registry: email/webhook/ntfy are now providers wrapping the existing send functions (no delivery logic rewritten), and the event text is rendered once per recipient instead of once per channel
  • Enabling the plugin is the opt-in — no separate admin toggle. Admin-scoped events (e.g. version available) are never deliverable to a plugin channel, and a plugin can't shadow a built-in channel id
  • The hook runs userless: the host resolves the recipient's decrypted per-user settings and hands them to the plugin, so a channel plugin can be given your push token without being able to read your trips as you

Plugin security & operations

  • Operator-supplied egress hosts (operatorEgress): a plugin's allowed hosts are fixed in its manifest at publish time, which can't work for a self-hosted service — a community Gotify/ntfy plugin cannot know your hostname. Such a plugin now declares the flag and an admin adds the real hosts post-install (Admin → Plugins → ⋯ → Allowed hosts). Only an admin, never an end user; same validation as manifest egress; dropped on uninstall. Discover shows a "+ hosts you add" pill so the reach is visible before install
  • Per-plugin RPC rate limit (token bucket) — a misbehaving plugin can no longer starve the host event loop; the log channel (ctx.log, stdout/stderr) is throttled the same way, so a while(true) log loop can't freeze the instance either
  • Plugin settings keys are validated at install and the stored config is parsed null-prototype — a field named __proto__ could previously resolve off Object.prototype and report as configured for users who had configured nothing
  • Plain-HTTP egress worked for no plugin at all (the guard misread Node's normalized net.connect() args and refused declared hosts as "localhost"); it failed closed, so it was never a hole. Fixed, and TREK_PLUGIN_ALLOW_PRIVATE_EGRESS — documented but wired to nothing, since the child's env is scrubbed — is now actually forwarded, so a LAN service is reachable when an operator opts in
  • Lifecycle hardening: install/enable/disable/crash paths recover cleanly
  • Uninstall purges plugin secrets; cron leak fixed; slot filtering and manifest normalization tightened (audit follow-ups)
  • Bridge path hardening and a frame CSP for plugin-served static assets
  • User-visible plugin activity log — see what each plugin read, wrote and invoked
  • Backups now include each plugin's data and code; restore stages them and swaps on the next boot. Staging is atomic and the archived DB is a consistent VACUUM INTO snapshot, so an interrupted or concurrently-written backup can't corrupt or silently delete a plugin's only data copy
  • Lifecycle races closed: a late load event can't resurrect a stopped plugin or double-fire its jobs; a re-enable during shutdown can't lose its DB handle; erasure drains coalesce; a restore quiesces running plugins even for older archives — no "ghost" plugins
  • The SDK mock host and dev runner now match the real host's rules (userless jobs/events, member/notify/db.tx/ws guards, validation), so a plugin behaves the same locally and in production

Securit...

Read more

v3.2.1

Choose a tag to compare

@mauriceboe mauriceboe released this 05 Jul 22:36
f4d1c0b

Plugins go from read-mostly observers to a real extension platform: they can now write to the itinerary, contribute native data, react to core events, add whole planner tabs, and match TREK's look — all behind the same sandboxed, permission-gated boundary. Plus budget, collections, files, guest and mobile fixes. No breaking changes — every new capability is opt-in and admin-consented; existing plugins keep working.

Plugins: 3.2.0 vs 3.2.1

Please consult the wiki for more information.

Capability 3.2.0 3.2.1
Usable permissions ~8 ~18
Write / mutate methods 1 (costs.create) ~16 — costs C/U/D, places C/U/D, days C/U/D, itinerary assign/unassign, trips.update, metadata C/U/D
Provider hooks 0 (declared but dead) 2 live — placeDetailProvider, warningProvider (+ invoke.hook transport)
Event subscriptions events:subscribe (name + trip only)
Read subsystems trips, costs, users + packing, files
Entity metadata db:meta (no schema fork)
UI extension points slots sidebar/hero, pages + trip-page type, + place-detail slot
Design & DX minimal UI design kit, window.trek.ui, typed SDK, Cookbook + example
Distribution registry install + sideload .zip, registry force-refresh
Plugin dependency Inter plugin dependencies (for potential plugin libraries)
Addon dependency Ensure specific addon is enabled

Plugin platform

  • Permission-gated planner write APIs (places, days, itinerary, trips), each gated by the acting user's own *_edit permission (#1429)
  • trip-page plugins (a tab inside every trip) and a place-detail widget slot
  • Provider hooks wired: placeDetailProvider and warningProvider, also at GET /api/place-details/:id and /api/trip-warnings/:id
  • Core event subscriptions (events:subscribe) — react to place:*, day:*, budget:*, file:*, …
  • Entity metadata (db:meta) — per-plugin key/value store on trips/places/days
  • Broader reads: ctx.packing.list / ctx.files.list; costs read + write (get/list/create/update/delete)
  • TREK UI design kit (<!-- trek:ui --> → tokens, glass, dark-mode, accent, appearance flags, window.trek), native scaffold, themed dev preview, auto-height glassy cards
  • Author DX: typed ctx returns, window.trek.ui DOM helpers, Plugin Cookbook + trip-doctor example, clack/prompts CLI
  • Sideload a plugin by uploading a .zip; force-refresh the registry

Plugin security

  • Open redirect prevented in the plugin route proxy
  • hook:* grant enforced — implementing a hook isn't enough without the consent
  • packing.list respects per-user visibility (#858) — no leaking another member's private items
  • IPC parent/child sealed, arbitrary API access blocked, full capability surface hardened + tested

Budget & costs

  • FX rate frozen on every cost and settlement write, so a later rate drift can't re-open a settled position (#1445)
  • New "Outstanding amount" card

Collections, files & wallet

  • Editing a saved place no longer resets its status to "idea"
  • Save picker shows above the mobile place detail
  • Apple Wallet .pkpasses supported, incl. booking uploads and the files tab (#1447, #1448)

Guests

  • Guest names are per-trip again — the same friend can be "Jake" on two trips instead of being auto-renamed to "Jake 2" (#1446)

Planner & mobile

  • Drag & drop disabled on mobile so the places list scrolls
  • Day-plan collapse state persists after closing the page
  • Remove-from-day button is icon-only on mobile (#1432)

Other

  • Keep stored settings when an update fails; Unsplash API key usage fixed
  • Translation fixes (Vietnamese "Disabled", remaining new keys across 22 locales)
  • Added a Code of Conduct

Full changelog: v3.2.0...v3.2.1

v3.2.0

Choose a tag to compare

@mauriceboe mauriceboe released this 04 Jul 23:25
Release 2 9 0 (5) (1)

TREK 3.2.0

TREK becomes a platform. A sandboxed Plugin System with a community registry, one-click installs and a full author SDK. Collections — a personal place library that lives outside your trips. Public transit routing right in the planner, no API key needed. AI booking import for the confirmations structured parsing can't read. On top: per-user Appearance & theming, a robust offline mode with real conflict resolution, video in journeys and files, subscribable calendar feeds, an in-app help centre — and Vietnamese as language number 22.

No breaking changes — all 19 database migrations run automatically on startup, and Collections and AI Parsing stay off until you enable them.


Plugin System

TREK is now extensible. Anyone can build a plugin — a dashboard widget, a full page, or a headless integration — and self-hosters can browse a community registry and install it with one click, without ever touching TREK's source. Plugins run with real OS-level isolation behind an explicit permission model, and the whole system ships with an author toolchain that takes you from scaffold to published registry entry in a handful of commands. (#1415)

  • Three plugin typeswidgets render on the dashboard (sidebar card or a hero slot on the boarding-pass bar), pages get their own entry in the navigation, and integrations run headless server logic (API routes + scheduled jobs)
  • Real isolation, not a label — every active plugin runs as its own OS process under Node's permission model: it cannot read trek.db or any TREK secret, write files, spawn processes, or load native addons; its data lives in a separate per-plugin SQLite file
  • Capability permissions — a plugin declares what it needs (own database, membership-checked trip reads, public user profiles, namespaced WebSocket broadcasts, per-host outbound HTTP) and TREK's RPC host only exposes what was granted — an ungranted call is unreachable, not merely refused
  • Egress guard with SSRF backstop — all outbound traffic (fetch, sockets, UDP, even DNS) is restricted to the plugin's declared hosts, and connections to loopback/private/link-local/metadata addresses are refused even for allow-listed hosts
  • Sealed plugin UI — pages and widgets render in sandboxed, opaque-origin iframes with a locked-down per-plugin CSP; a plugin's frontend can never read your session cookie or the surrounding TREK page
  • Registry with one-click installAdmin → Plugins → Discover browses the public TREK-Plugins registry; installing verifies the artifact's pinned SHA-256 (plus an optional Ed25519 author signature), safely unpacks it, and registers it inactive — nothing runs until you flip the toggle
  • Review before you install — the detail view lists in plain language what a plugin can access, which hosts it talks to, and what it will ask you to configure, alongside a Reviewed badge for maintainer-scanned versions and an honest disclaimer about what the sandbox does and doesn't guarantee
  • Re-consent on updates — an update that requests new permissions or new outbound hosts is installed but left off until you approve the exact delta; an update can never silently widen a plugin's access
  • A real management surface — search, type/status filters, Update all, per-plugin health, error log, a capability audit log, restart, and uninstall with the choice to keep or delete the plugin's data
  • trek-plugin-sdk on npmnpx trek-plugin-sdk create scaffolds a plugin, dev runs it locally against a mock host that enforces your manifest's permission grants exactly like the TREK host (no TREK instance needed; the OS sandbox and egress guard apply inside a real TREK), and publish does the entire release — pack → GitHub release → preflight → auto-opened registry PR — in one command
  • Koffi, the reference plugin — TREK's mascot ships as a worked example: an animated suitcase hero widget driven by your real trip data, published in the registry
  • On by default, with a kill switch — the plugin system is available out of the box, but installed plugins still have to be activated one by one, so nothing third-party runs until you turn a specific plugin on; set TREK_PLUGINS_ENABLED=false to switch the whole system off (installed plugins stay on disk, deactivated)
  • Hardened by adversarial audit — two dedicated audit passes (threat model: malicious plugin author, malicious artifact) before merge closed sandbox-escape, SSRF, decompression-bomb, DoS and consent-bypass findings

Docs: Plugins · Plugin Development · Plugin Permissions · Publishing a Plugin


Collections — A Personal Place Library

A place you saved used to exist only inside its trip. The new Collections addon gives every user a place library outside of any trip: keep named lists of spots you've discovered — a "Norway someday" wishlist, "Best coffee in Lisbon" — copy places into and out of any trip, and share lists with other users. Enable it under Admin → Addons to get the new /collections page. (#1081)

  • Named lists — each with a colour, description, links and a cover image (upload or search Unsplash); a built-in All saved view unions your whole library
  • Idea / Want to go / Visited — a per-place status you cycle with one tap, plus status and category filters
  • List + map layout — lists rail, places list and a persistent map side by side, with a full-map toggle and a place detail sheet; mobile keeps a list/map switch
  • Save from a trip — "Save to collection" in the place inspector and context menus, plus a bulk action that copies every selected trip place into a chosen list (duplicates are skipped)
  • Copy to trip — saved places copy into any trip with name, description, category, notes, price, coordinates, photo and tags; the trip's add-place modal gains a Saved places picker sorted by proximity to the trip
  • Bulk actions — select-all, bulk delete, bulk copy-to-trip, and move or duplicate a selection into another list
  • Sharing with member roles — invite other users to a list (accept/decline, like Vacay fusion) with per-member roles: viewer, editor or admin, enforced on the server and synced live
  • Dashboard widget — your lists as compact badges on the dashboard, hideable per user
  • Custom labels — each list can define its own labels (e.g. Berlin, Hamburg, Ostsee in a "Germany 2026" list) to group its places. Create/rename/recolor/delete them in a label manager, assign them from a place's detail sheet or in bulk from the selection toolbar, and filter both the list and the map by label (multi-select, any-match). Labels are shared with everyone on the list — managing and assigning needs edit rights, filtering is open to all members

Full guide: Collections


Public Transit Routing

Google-Maps-style public transit directions, right in the planner — with no paid provider. Routing comes from Transitous, the community-run MOTIS service built on open GTFS feeds: free, open data, no API key required. (#1065)

  • Automated transport mode — the Add-transport dialog gets a Manual / Automated switch; Automated embeds the transit search, and a new tram button on each day header opens it with that day preset
  • Journey search — from/to stop search with the day's own places and hotels as quick picks, depart/arrive time, mode filters (train, subway, tram, bus, ferry, cable car) and ranking by best route, fewer transfers or less walking
  • Readable results — itinerary cards with local times, duration, transfers and walking minutes, line badges in their official colors, and an expandable stop-by-stop breakdown with platforms, headsigns and operators
  • First-class transit entries — a saved journey is its own reservation type: the day timeline renders the legs inline and folds the full itinerary out in place, and the Transports tab lists journeys in their own section
  • Journey view — duration / transfers / walking stat tiles, inline title rename, notes with a markdown toolbar, and a "Change route" action that re-runs the search pre-seeded with the journey's endpoints
  • Real paths on the map — journeys draw along the actual rail and bus alignments in each line's color, with walks as dotted connectors, on both map renderers
  • Right times, right slot — times are station-local, transfer waits count toward the duration, and after-midnight arrivals land on the next trip day
  • Self-host friendly — the server proxies Transitous (authenticated, rate-limited, cached); point TRANSIT_API_URL at your own MOTIS instance for zero third-party egress

Also on the map: booking route endpoint labels now default to off for a calmer look — re-enable them anytime in Settings.

Docs: Transport: Flights, Trains & Cars


AI Booking Import

Booking import no longer gives up on documents KDE Itinerary can't read. The new opt-in AI Parsing addon adds an LLM fallback that extracts flights, trains, buses, ferries, rental cars, hotels, restaurants and event tickets from unstructured confirmations — with first-class support for self-hosted local models. Based on work by @jubnl. (#1301, #1318)

  • Bring your own model — Local (Ollama), OpenAI — including any OpenAI-compatible endpoi...
Read more

v3.1.4

Choose a tag to compare

@jubnl jubnl released this 01 Jul 17:03

Changelog

  • fix(backups): prevent recursion in path that is backed up
  • fix(share): show user currency instead of the default euro in the share page
  • fix(files): show descriptive error for unsupported upload type

What's Changed

Full Changelog: v3.1.3...v3.1.4

v3.1.3 — 8,000 stars 🌟

Choose a tag to compare

@mauriceboe mauriceboe released this 27 Jun 19:17

8,000 stars — thank you 🌟

TREK just passed 8,000 GitHub stars. For a self-hosted travel planner that started as a side project, that's genuinely humbling — and it's all of you: the bug reports, the translations, the pull requests, and everyone quietly running TREK on a NAS or a Pi in a closet somewhere.

A patch release is normally fixes only. As a small thank-you, 3.1.3 ships a few new features too — alongside the usual pile of fixes. Keep the issues and PRs coming. 💛

✨ New features

  • Distance unit — metric or imperial. A per-user setting so distances render in kilometres or miles, mirroring the temperature unit. Default stays metric. (#1300 — thanks @mvanhorn)
  • MapLibre + OpenFreeMap map provider. A third, fully self-hostable and API-key-free map provider alongside Leaflet/OSM and Mapbox. The GL engine is code-split, so Leaflet-only setups never download it. (#1317 — thanks @hykilpikonna)
  • Places sidebar follows the map. Click a marker and the places list scrolls straight to it. (#1315 — thanks @hykilpikonna)
  • Precise Google Maps links. The "Google Maps" button now resolves to the actual place via its feature ID instead of dropping a raw-coordinate pin. (#554 — thanks @hykilpikonna)

🔒 Security

This release also closes one privately-reported vulnerability. Updating is recommended.

Advisory Severity Issue
GHSA-r4cp-666p-8f69 Medium (CVSS 4.3) Cross-trip reservation title disclosure — a member of one trip could reference another private trip's reservation id (on file upload, update or link) and read its title back through the file's reservation join. Cross-trip references are now rejected on every file write path. (CWE-639)

Changelog

  • feat: distance unit (metric/imperial) display setting (#1300)
  • feat(maps): MapLibre + OpenFreeMap as a self-hostable map provider (#1317)
  • feat(planner): seek the places sidebar to the selected map marker (#1315)
  • feat(maps): precise Google Maps place links via feature IDs (#554)
  • fix(map): drop the phantom hotel leg to/from a transport endpoint on arrival days (#1321)
  • fix(map): draw the hotel-to-hotel leg on a transfer day with no activities (#1297)
  • fix(map): pin the GL basemap label language to the UI language (#1299)
  • fix(planner): show the route tools for a single place when optimizing from accommodation (#1330)
  • fix(maps): make Overpass endpoints configurable and harden the POI search (#1309)
  • fix(atlas): assign border places by polygon, not just bounding box (#1331)
  • fix(reservations): keep dated bookings on their date when the trip range shifts (#1288)
  • fix(costs): freeze the FX rate so settled expenses don't reopen when rates drift (#1335)
  • fix(airtrail): import the airline name, not the ICAO code (#1334)
  • fix(dashboard): persist the currency & timezone widgets so an upgrade keeps them (#1311)
  • fix(places): guide single-place links to the right importer (#1304)
  • fix(share): translate the day label on the public share page (#1296)
  • fix(pwa): use the self-contained app icon for the favicon so it shows on dark tabs (#1328)
  • fix(files): reject cross-trip reservation/place/assignment links — GHSA-r4cp-666p-8f69
  • i18n: new Swedish translation + refreshed Dutch translations
  • chore: replace the 3.0 upgrade notices with a thank-you / support modal
  • docs(wiki): document all environment variables and fix the kitinerary install steps

What's Changed

Released by @mauriceboe in #1324, with community contributions from @hykilpikonna, @mvanhorn, @olsson82, @eindpunt, @Hardik-369, @jubnl and @CooperSheroy. Thank you all. 🙌

Full Changelog: v3.1.2...v3.1.3

v3.1.2

Choose a tag to compare

@mauriceboe mauriceboe released this 23 Jun 19:28

Changelog

  • fix(budget): accept comma decimal separator in expense amounts
  • fix(budget): scale category bars relative to top category
  • chore: dockerignore spec.ts files
  • fix(atlas): gzip-compress responses so large country GeoJSON loads behind reverse proxies (#1262)
  • chore: add ca_profile.xml for Unraid Community Apps submission
  • fix(security): allow same-origin PDF previews under CSP (#1253)
  • fix(dashboard): count archived trips in travel stats (#1264)
  • fix(days): align note time limit to 250 and keep toasts above modal blur (#1252)
  • fix(pdf): show photos for OSM places in the trip PDF (#1130)
  • fix(chart): allow setting storageClassName on PVCs (#1261)
  • test(days): bump over-long-time assertion to the new 250 limit (#1252)
  • fix(costs): move the unfinished marker to the category icon on mobile (#1266)
  • fix(dashboard): add a text-shadow so spotlight and card titles stay legible (#1267)
  • fix(atlas): give the country-GeoJSON fetch a longer timeout (#1254)
  • feat(helm): add annotations support for PVCs (#1270)
  • fix(admin): show non-Docker update steps when not running in Docker (#1269)
  • feat(planner): bring back the Google Maps route export button (#1255)
  • fix(auth): keep the last admin when OIDC claims would demote it (#1274)
  • fix(dashboard): show an error instead of a blank trip list when the server is unreachable (#1283)
  • fix(packing): keep a custom category when its last item is removed (#1289)
  • fix(costs): allow recording an expense with no split or payer (#1286)
  • fix(map): draw the route line to and from the day's accommodation (#1275)

What's Changed

Full Changelog: v3.1.1...v3.1.2

v3.1.1

Choose a tag to compare

@jubnl jubnl released this 18 Jun 18:21

v3.1.1 - Hotfixes

⚠ Proxmox VE community-script users: You can now update safely to 3.1.1!!

Changelog

  • fix(shared-view): render each leg of multi-leg flights correctly
  • feat(pdf): add legs to pdf export
  • fix(demo): skip first-run admin seed in demo mode
  • fix(docker): ship the encryption-key migration script in the image
  • fix(vacay): keep the mode toolbar above the mobile bottom nav
  • fix(dashboard): show the correct reservation date regardless of timezone
  • fix(atlas): cursor-following tooltips and removing countries from search
  • fix(oidc): keep dots in generated usernames
  • fix(collab): show poll option labels in the UI
  • feat(backup): make the upload size limit configurable
  • feat(costs): create an expense from a booking, fix editing total-only items
  • test(reservations): align syncBudgetOnUpdate unit tests with no-wipe + type-sync
  • fix(planner): keep a reservation on its day when edited
  • fix(planner): derive a booking day from its date when none is set
  • fix(planner): let a booking's day follow its date when edited
  • fix(atlas): keep the continent breakdown in sync on mark/unmark
  • feat(admin): let admins set a default currency for new users
  • fix(atlas): give every sub-national region a distinct code
  • fix(dashboard): never crash on a malformed reservation date
  • fix(airtrail): gate airtrail update behind a user setting, on airtrail update: rebuild payload from fresh data to prevent any data loss
  • fix(costs): rework the cost panel UX wise and apply prettier on the shared package
  • fix(airtrail): don't use cabin class as seat on import
  • fix(airtrail): import scheduled flight times instead of actual
  • fix(planner): hydrate per-assignment times when editing a place from the pool
  • fix(mcp): make write tools return client-valid, hydrated entities
  • fix(mcp): fix ICS/maps/accommodation bugs, add settlement & template tools
  • chore: bump nodemailer
  • chore: bump multer

What's Changed

Full Changelog: v3.1.0...v3.1.1

v3.1.0

Choose a tag to compare

@mauriceboe mauriceboe released this 16 Jun 20:28
Release 3 1 0 (1)

TREK 3.1.0

TREK 3 — rebuilt from the inside out. A modern NestJS backend, a React 19 frontend, and an end-to-end shared Zod contract layer so the client and server can never drift. On top of that foundation: a Splitwise-style Costs rework, Passkey login, AirTrail flight sync, multi-leg flights, real road routing, a reworked dashboard, five new languages, and a comprehensive offline/PWA hardening pass.

This release also fixes 18 privately-reported security vulnerabilities — including a critical authentication bypass. Updating to 3.1.0 is strongly recommended for every self-hosted instance. See Security below.


⚠ Proxmox VE community-script users: please wait before updating to v3.1.0

v3.1.0 changes how TREK is built and run:

  • the repo is now an npm workspaces monorepo (client + server + new shared package) with a single root lockfile,
  • the server is now compiled (dist/) instead of run directly via tsx,
  • there are new build/runtime requirements (e.g. kitinerary-extractor for booking import).

The community-scripts (https://community-scripts.org/scripts/trek) LXC helper for TREK still targets the old 3.0.x layout. If you run its Update option right now, the build will fail and may leave your container in a broken state.

What to do: stay on your current version for now. An updated script is being submitted to ProxmoxVED and will roll out to ProxmoxVE after review — we'll post here once it's live. Your data (/opt/trek/data, /opt/trek/uploads) and .env are safe in the meantime.

Docker, Unraid, Helm and manual installs are not affected.


TREK 3 — NestJS + React 19

The biggest architectural change in TREK's history. The Express backend has been migrated to NestJS, the frontend upgraded to React 19, and both halves now share a single Zod contract package as the source of truth for every request and response.

  • NestJS backend — modular architecture with dependency injection, replacing the previous Express app (#1087, foundation harness #1050)
  • React 19 frontend — upgraded across the whole client
  • Shared Zod contracts — one typed, validated schema package consumed by both client and server, so API shapes stay in lockstep
  • Monorepo build pipelineshared is now a properly built package; i18n moved into it so every surface reads from one translation set (#1056, #1066)
  • Pilot-then-migrate — the cutover started with a single module (/api/weather) to prove the harness before the full migration (#1053)

Migrations run automatically on startup — no manual steps.


Security — Update Strongly Recommended

This release closes 18 privately-reported vulnerabilities, disclosed through coordinated private reporting. They range from a critical authentication bypass down to low-severity hardening. All self-hosted instances should update to 3.1.0.

Critical

Advisory Issue
GHSA-mjh4-w6fq-54qm MFA bypass — the pre-2FA mfa_token was accepted as a full session token (JWT purpose confusion)

High

Advisory Issue
GHSA-fvgw-r58q-4cw4 OIDC account takeover — an SSO identity could be linked to an existing local account without email_verified
GHSA-627x-pmfq-98qv SQL injection via unvalidated column names in journey entry update

Medium

Advisory Issue
GHSA-xmqx-m462-52qr OIDC login CSRF / session fixation — state was not bound to the initiating browser
GHSA-6vv9-669j-468p Arbitrary file deletion under /uploads via attacker-controlled trip cover_image
GHSA-mggj-xgv8-95fp MCP share-link tools let a non-owner member mint a public trip share link
GHSA-jx25-7cj6-88hq Cross-trip IDOR — budget member paid-flag toggle not scoped to the trip
GHSA-p7pv-2jpm-qjx3 Journey share permission flags (timeline/gallery/map) enforced only client-side
GHSA-pfr2-fgv9-q5xv Stored HTML injection via reservation endpoint labels rendered into map markers
GHSA-h66w-m5g2-cqpc Path traversal in the public journey photo proxy (local provider) reads auth-gated uploads
GHSA-3frp-pcr3-w932 Stored HTML injection via journey entry story in the PDF export
GHSA-24x9-fcj9-vp6w IDOR in the journey photo proxy — a share token resolves arbitrary asset IDs
GHSA-6f5j-xgjg-h9q7 MCP tools bypass the admin-configurable RBAC permission system
GHSA-rrrf-7cf6-8736 A settings password change did not invalidate existing sessions / tokens
GHSA-f5vh-p2h5-x735 Blind SSRF via unvalidated redirect-following in Google/Naver list import and Maps URL resolution
GHSA-mx6m-qxv8-w624 IDOR — any authenticated user could read another user's journey share token (full journey leak)

Low

Advisory Issue
GHSA-w6f3-fp9r-xp6p Admin backup restore extracted archives without a decompression-size limit (zip bomb)
GHSA-g8rf-gqrw-4qf9 Stored cross-user HTML injection via trip title in the Journey suggestion banner

Reported through coordinated private disclosure by @bytejmp, @tonghuaroot, @therawdev, and @shukla304. Thank you for reporting these responsibly.


Costs — Splitwise-Style Expense Splitting

Budget has been reworked into Costs: a proper shared-expense tracker built for group trips.

Please note: Due to the major transition from a budgeting tool to a cost tracker, previous settings in the budget add-on (e.g., cost splitting) cannot be automatically carried over and get a reset. All entries will be retained in the new add-on but will be assigned to the "Other" category.

  • Splitwise-style splitting — record who paid and split a cost across trip members, with running per-person balances
  • Settle up — one-tap minimal-transfer settlement with persisted history and undo
  • Multi-currency — log expenses in any currency (#551)
  • Multi-payer support — costs aren't tied to a single payer
  • Fixed categories — a consistent category set for cleaner reporting
  • Mobile-first — redesigned for phone use end to end

Passkey (WebAuthn) Login

  • Passwordless sign-in — register a passkey and log in with Face ID / Touch ID / a security key (#297, #1111)
  • WebAuthn standard — platform and roaming authenticators supported
  • Coexists with existing auth — passkeys sit alongside password, SSO, and MFA

Authentication

  • Remember me option — Add a Remember me checkbox on login screen to extend session expiry (#1189)

AirTrail Integration

Two-way flight sync with AirTrail (#214, #1158).

  • Import flights — pull your flight history from AirTrail into TREK
  • Two-way sync — flights flow in both directions and stay aligned
  • Reservation-aware — imported flights land as transport reservations

Planner & Maps

  • Real road routes (OSRM) — when route display is enabled, day routes follow actual roads with travel-time connectors instead of straight-line estimates; the hidden route-calculation setting is gone, so enabled routes always use real road geometry (#1060, #1064)
  • Multi-leg (layover) flights — model connecting flights with layovers as a single booking (#872, #1146)
  • Reorder whole days + insert a day — drag entire days around and insert a new day into a trip (#589, #1148)
  • Explore places on the map — discover and add points of interest (OpenStreetMap / Overpass) directly from the planner map; instance-wide Mapbox token support (#1147)
  • More transport types — bus, taxi, bicycle, ferry and others (#1105)
  • Richer imported places — list-imported places are enriched via the Places API (#886, #1161)
  • Route optimization around accommodation + confirm-before-delete on notes (#1123)
  • Category-coloured markers — map marker borders reflect the place's category colour (#1062)
  • Mobile route tools — planner route tools are reachable from the mobile day-plan sheet (#1142)

Reservations

  • Native booking-confirmation import — drop in a booking confirmation and TREK extracts the details via KDE KItinerary (#1102)

Dashboard Rework

  • Boarding-pass hero — the next trip rendered as a boarding-pass-style h...
Read more

v3.1.0-pre.3

v3.1.0-pre.3 Pre-release
Pre-release

Choose a tag to compare

@jubnl jubnl released this 16 Jun 06:48
9f5d2f6

⚠ PRE-RELEASE — USE AT YOUR OWN RISK ⚠

This is a pre-release build of TREK. It may contain bugs, incomplete features, or breaking changes. It is not recommended for production use.

  • Do not use this on a live instance without a full backup
  • Database migrations may not be reversible
  • APIs and configuration options are subject to change without notice

If you encounter issues, please report them at https://github.com/mauriceboe/TREK/issues.

Stable releases are available on the main release page.

⚠ Pre-release image — not recommended for production use.

Tag Description
3.1.0-pre.3 Pinned to this exact pre-release build
3-pre Latest pre-release on the v3 line
latest-pre Latest pre-release across all versions
docker pull mauriceboe/trek:3.1.0-pre.3

What's Changed

  • feat(auth): add "Remember me" checkbox to extend session lifetime by @jubnl in #1189
  • fix(backup): restore from Docker, fail-fast on shadowed /app, bundle encryption key (#1193) by @jubnl in #1197
  • fix(places): fall back to search when autocomplete details lookup fails (#1192) by @jubnl in #1198
  • fix(planner): scroll long place description/notes on mobile (#1195) by @jubnl in #1199

Full Changelog: v3.1.0-pre.2...v3.1.0-pre.3