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 I and Julien 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:contentscope;tripsgainsgetDays/getReservations/getAccommodationsand 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-detailandreservation-detailwidget slots;trip-pageplugins 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, andrates.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
iconfield for glyphs; a plugin's own sandboxed/uiframe 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 awhile(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 offObject.prototypeand 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, andTREK_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 INTOsnapshot, 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
Security
- Fixed a broken-access-control issue (IDOR): a trip member could delete a file attached to a note in a different trip by id. Note-file deletion is now scoped to the trip. (GHSA-cjc5-722j-vvmf)
- Fixed an SSRF via the user-configurable LLM base URL: it can no longer resolve to the link-local / cloud-metadata range (169.254.169.254 / IPv6 equivalents, plus the Alibaba metadata IPs), while a local or LAN model server stays reachable. Redirects are followed manually and re-checked at every hop, so a configured endpoint can't 302 its way to the metadata service. (GHSA-fmq9-ggh3-647p)
- Fixed an OAuth scope bypass in the MCP
get_trip_summarytool: it returned a trip's members (with their email addresses), full itinerary and accommodations to any valid MCP token regardless of the scopes it was granted. The core trip data is now gated ontrips:read(only the id + title stay available for navigation without it). Thanks @kaimandalic. (GHSA-qvw8-w937-vcmq) - Fixed a share-link permission gap: public trip shares ignored
share_map, so the day-by-day itinerary and every place (with coordinates and addresses) came back even when the owner turned the map off β it was hidden only in the UI. It's now enforced server-side like the other share toggles, and the same check was added to the journey photo/asset proxies (share_gallery) and the shared place-photo proxy (share_map). Thanks @kaimandalic. (GHSA-9hc8-p7gm-p7mx)
Developer experience
dev-link: hot-reload a local plugin against real data, managed from the admin UI- Registry browse shows download counts (sortable) and an issue-URL link per plugin
- SDK 1.3.0 β 1.4.0: motion library,
confirm(),openExternal(), fill pages, live context, native<select>auto-upgrade, preflight type validation - SDK validator now matches the server capability surface exactly β it previously only knew the 3.2.1 permission set, so
validate/packrejected any manifest using a newer scope --template notification-channelscaffolds a working channel plugin (hook grant, per-host egress, per-user secret settings, a Test connection action)- New permission labels in all locales; Permissions wiki page updated
Budget & costs
- The cost currency picker offers every currency the FX conversion supports (~160 codes, incl. OMR, CRC, UGX, MKD, ALL) instead of a hardcoded 47-code list; already-saved archived currencies stay selectable. Closes #1470
- Settlements honor custom per-member splits instead of silently falling back to an equal split; the Overview per-person columns and CSV leave custom-split items blank where an averaged figure would mislead. Closes #1458
- The admin add-on catalog names the Costs add-on "Costs", matching the trip tab and navbar in every language. Closes #1464
- The expenses CSV export is back β it was dropped in the BudgetβCosts rework and returns to the Costs view. Closes #1500
Collections & files
- The Add-place button stays reachable after a collection's first save β the toolbar no longer disappears on desktop once the list gains a mapped place. Closes #1485
Planner & mobile
- Two quick one-finger pans on mobile no longer trigger a phantom double-tap zoom on the trip map (Leaflet and GL renderers). Closes #1440
- Day routes respect hotel check-in/check-out times β no more spurious hotel legs to places visited before check-in or after check-out, on the map, the sidebar connectors and the Google Maps export alike. Closes #1465
- The itinerary PDF export repeats the day header when a day's entries overflow onto the next page. Closes #1471
- Transport and booking cards sort chronologically β day-only entries (no typed time) no longer cluster at the top in creation order. Closes #1507
- Route distance stays visible across a car rental's middle days (the through-leg is no longer re-keyed to a row that isn't rendered on those days). Closes #1504
- Automated-transport quick-pick stops are scoped to the selected day instead of the whole trip. Closes #1460
- The booking modal's address fields autocomplete like the other modals, and a typed address is no longer dropped on save. Closes #1496
- Mobile view fixes: the home page no longer has a second scroll layer, the Plan/Places drawer respects the bottom safe area, and long packing/list names no longer overflow. Closes #1505
- Mobile numeric fields select on focus, so typing "6" over "1" replaces it instead of yielding "61". Closes #1513
Journeys & memories
- Journal skeleton suggestions stay in sync with the linked trip β unassigning, moving or re-timing places (including via MCP) updates the skeletons instead of letting them drift; filled entries are never destroyed. Closes #1473
- Hidden Immich assets β like Live Photo motion parts β are filtered out of Memories, removing their broken thumbnails. Closes #1474
- Immich albums no longer show "No photos yet": Immich v3 dropped the
assetsproperty from the album response, so album contents always parsed as empty and album-link syncs silently added nothing. Closes #1492
Calendars & transit
- Subscribed calendar feeds emit TZID + VTIMEZONE, so iOS and Google Calendar show events at the time TREK shows instead of shifting them into the subscriber's local zone. Closes #1453
- The holiday-calendar state/region picker is built from ISO 3166-2, so states without a state-specific holiday that year (e.g. US-WA) no longer vanish and block calendar creation. Closes #1456
- Arrive-by transit searches interpret the target time in the destination's timezone. Closes #1479
- The public-transit option only appears once a trip actually has dates to search against.
- Small airports are back in the bundled airport data, so they resolve in search and export again. Closes #1521
Atlas
- Visited sub-regions highlight even when the cached region name differs only by accent or dash style ("Ile-de-France" vs "Γle-de-France") β names are normalized before matching. Thanks @daften. Closes #1487
- A country you only changed planes in no longer counts as visited β only real from/to flight legs count, not intermediate connecting stops. Thanks @daften. Closes #1366, #1486
- Atlas and the trip map failed to load on some setups β the offline service worker cached an opaque cross-origin response for Leaflet's stylesheet; it's now self-hosted (bundled), so the maps render. Closes #1497
- Southern-Spain trips were mislabeled as Algeria β the missing Spain bounding box is restored, so e.g. a MΓ‘laga flight resolves to Spain. Closes #1490
MCP
- MCP connections no longer break roughly once an hour: a routine OAuth access-token refresh no longer revokes the live MCP session (replay detection still does). Thanks @daften. Closes #1475
Translations
- Vietnamese locale corrections (several mistranslations fixed, e.g. "Backup" was shown as "Support") β thanks @trongbinh15 β and Simplified Chinese improvements throughout β thanks @sld272.
Other
- The next-trip boarding-pass stats bar on the dashboard renders on Safari again (its ticket-stub mask was mis-composited to fully transparent). Closes #1459
- The sliding active-tab pill in the navbar re-measures after the web font loads and on resize, so it no longer sits slightly offset after a hard reload. Closes #1481
- Large trip-cover uploads no longer throw an error β the shared request timeout no longer aborts a big multipart upload. Closes #1495
- Plugin UI polish: full-page (
trip-page/page) plugins fill the frame instead of collapsing into a floating island, and trip warnings no longer sit on top of the planner and its map toolbar β a warning from a plugin that owns a tab becomes a chip in the navbar, the rest float at the bottom - Deployment: the Helm chart defaults to the
Recreatestrategy so a rolling update can't put two pods on the same ReadWriteOnce SQLite volume, and the rootdocker-compose.ymlis pinned to:latestinstead of a:devtag no workflow builds β a clone-and-up at the release tag was running a stale image
Full changelog: v3.2.1...v3.3.0