github mauriceboe/TREK v3.3.0
v3.3.0 — 10,000 stars 🌟

latest release: plugin-sdk-v1.4.1
4 hours ago

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: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

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_summary tool: 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 on trips: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/pack rejected any manifest using a newer scope
  • --template notification-channel scaffolds 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 assets property 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 Recreate strategy so a rolling update can't put two pods on the same ReadWriteOnce SQLite volume, and the root docker-compose.yml is pinned to :latest instead of a :dev tag no workflow builds — a clone-and-up at the release tag was running a stale image

Full changelog: v3.2.1...v3.3.0

Don't miss a new TREK release

NewReleases is sending notifications on new releases.