docs(wiki): bring the pages in line with 4.3.0

Every page was read against the code on dev. What changed:

- Pages that described 4.2 behaviour: satellite view on the vector maps,
  the mobile journal timeline, Collab with its fifth tab, the Review badge,
  clustered pins on the share page, the Leaflet preset list, the strict
  guard on fe80::/10, the invite link tools and their scopes, the Public
  API scope rules, the permission count in the plugin SDK.
- Features of this release that no page mentioned: videos in a journal
  entry, receipts on an expense, the Google transit backend, routing
  counters, the rating on a place, the navigation menu, car stops, the
  manual school holiday catalogue and its MCP tools, the Dawarich panel in
  the Atlas, the release notes modal, plugin frames going fullscreen, the
  corridor call into the search provider hook.
- Statements that sent an admin the wrong way: DEFAULT_LANGUAGE is not in
  the Helm ConfigMap, env.PORT cannot be changed on the chart alone, the
  rotation script does not cover the document sync credentials, event
  handlers cannot broadcast, there is no per user MFA reset in the admin
  panel, the demo user can edit, an unparsable APP_URL stops the server.
- Updating gets a section for 4.3.0: the TREK Places API as the new
  default, the new variables, the Overpass timeout, SSO sessions on an
  OIDC only instance, the migration that puts booked nights on the drive.
This commit is contained in:
Maurice
2026-09-20 12:11:41 +02:00
parent 033735777a
commit 44c92052a3
45 changed files with 252 additions and 81 deletions
+1 -1
View File
@@ -36,6 +36,6 @@ The following addons are registered in the system (defined in `server/src/db/see
## Per-addon sub-features
Some addons expose sub-features that an admin can independently toggle. The [Real-Time-Collaboration](Real-Time-Collaboration) addon, for example, lets an admin decide which of its four sub-features (chat, notes, polls, and what's next) are active across the instance. These are configured from the [Admin-Addons](Admin-Addons) panel alongside the addon's main toggle.
Some addons expose sub-features that an admin can independently toggle. The [Real-Time-Collaboration](Real-Time-Collaboration) addon, for example, lets an admin decide which of its five sub-features (chat, notes, links, polls, and what's next) are active across the instance. These are configured from the [Admin-Addons](Admin-Addons) panel alongside the addon's main toggle.
The **Documents** addon carries one switch per document store: Paperless-ngx, Papra, Nextcloud, OpenCloud and Synology Drive. They are off by default and only show while Documents is on, and switching Documents off switches all of them off. The stores themselves are connected per trip, in the trip's file manager. See [Document-Sync](Document-Sync).
+2 -1
View File
@@ -21,9 +21,10 @@ The default trip addons are: **Lists**, **Costs**, **Documents** and **Collab**
**Sub-toggles on trip addons:**
- **Lists**: when enabled, a nested **Bag Tracking** toggle appears. Bag Tracking lets users assign packed items to specific bags.
- **Collab**: when enabled, four sub-toggles appear for individual collaboration features:
- **Collab**: when enabled, five sub-toggles appear for individual collaboration features:
- **Chat**: in-trip real-time chat
- **Notes**: shared trip notes
- **Links**: shared trip links, on by default
- **Polls**: trip polls
- **What's Next**: the "what's next" widget
- **Documents**: when enabled, one row per document store appears underneath: **Paperless-ngx**, **Papra**, **Nextcloud**, **OpenCloud** and **Synology Drive**. All of them are off by default. Switching a store on only lets trip owners offer it; the address and credentials are entered per trip, in the trip's file manager. Switching a store off pauses the trips bound to it without touching them. Switching **Documents** itself off switches every store off, and each has to be switched on again afterwards; a store cannot be switched on while **Documents** is off. See [Document-Sync](Document-Sync).
+5 -1
View File
@@ -24,7 +24,7 @@ Below the support cards, a chronological timeline lists GitHub releases for the
- **Version tag** (e.g., `v2.9.14`)
- A **Latest** badge on the first (most recent) entry in the displayed list
- **Release date** and author
- A **Show details / Hide details** toggle that expands the release notes (Markdown rendered inline)
- A **Show details / Hide details** toggle that expands the release notes (a small Markdown subset rendered inline: `##` and `###` headings, `-` or `*` list items, **bold**, `code` and http(s) links; anything else is shown as plain text)
Pre-release entries are hidden unless the server has both found a newer version and is itself running a pre-release. In every other case, including a pre-release install that is already on the newest pre-release, the timeline shows stable releases only.
@@ -38,6 +38,10 @@ The server checks for available updates daily at 9 AM (server timezone, defaults
Results are cached for 5 minutes to avoid repeated API calls.
## Release notes notice
After an update, every user is shown a **release notes** notice once: a modal on the desktop with what the release brought (three headline cards), a note from the maintainer, the support links and a **Release notes** link to the release on GitHub. Closing it keeps it closed for the version that is running; the next version brings it back once, a patch release included. It replaces the 4.0.0 notice. There is no switch for it in the admin panel.
## When to check
Review the GitHub tab before performing an upgrade to read the release notes for any versions between your current install and the target version. See [Updating](Updating) for the upgrade procedure.
+7 -1
View File
@@ -15,7 +15,7 @@ The Admin Panel is divided into tabs. Most tabs are always visible; a few appear
| Tab | Purpose | Conditional? |
|-----|---------|--------------|
| **Users** | Manage users, invite links, and permissions | No |
| **Personalization** | Packing templates and place categories | No |
| **Personalization** | Packing templates, place categories and the manually maintained school holiday catalog (see [Vacay](Vacay#manually-maintained-school-holidays)) | No |
| **User Defaults** | Default settings applied to new users | No |
| **Addons** | Enable or disable optional features instance-wide | No |
| **Plugins** | Install, update, and manage plugins; rescan the plugins folder; view each plugin's error log. See [Admin-Plugins](Admin-Plugins) | No |
@@ -38,6 +38,12 @@ With both fields empty, TREK uses public OSRM servers for routing and the public
> **AI / MCP:** These fields configure the routing services the planner and the road trip MCP tools use, and do not change stored trip data.
### Routing usage counters
Every route is calculated in the browser against the routing hosts, so the server never sees a routing request itself. To still know how much routing an instance does, the browser reports its tally in batches and the server keeps **daily counters**: how many requests, of what kind (route, segments, legs, alternatives), for which profile (driving, walking, cycling), how many waypoints, roughly how many kilometres, how many came back without a route, and whether a self-hosted engine answered. Counters only: no query, no coordinate, no route, no user and no trip is stored, and nothing leaves the instance. A day's row is kept for 400 days.
Counting is **on by default** and has no switch in the admin panel. To turn it off, set the `route_usage_enabled` key to `false` in the `app_settings` table; reports are then acknowledged but nothing is written. There is no admin screen for the totals either: an admin reads them at `GET /api/route-usage/summary` and wipes them with `DELETE /api/route-usage`.
### Linking to a tab directly
Every panel is reachable by URL, so a bookmark, an onboarding mail or a support reply can point at the one tab it is about instead of at the top of a page with thirteen of them:
+1 -1
View File
@@ -70,7 +70,7 @@ Enabling can be refused for good reasons. The first three are version gates that
Installed-but-disabled dependencies are enabled automatically as a cascade, and a toast tells you which.
The row's **⋯** menu offers **Restart** (active plugins only), **View error log**, **Allowed hosts**, links to the **Source repository** and **Report an issue** for registry plugins, and **Delete**.
The row's **⋯** menu offers **Restart** (active plugins only), **Instance settings** (plugins with instance-wide settings or actions), **View error log**, **Allowed hosts**, **Change version** (registry plugins only), links to the **Source repository** and **Report an issue** for registry plugins, and **Delete**.
## Updating
+10
View File
@@ -44,6 +44,15 @@ At zoom level 5 and above, the map switches to a sub-national region view (state
The bucket list is separate from "visited". Use it to track countries or places you want to visit in the future. Each bucket list item can have a name, coordinates, country code, optional notes, and a target date.
## Dawarich
With the [Dawarich](Dawarich) addon on and your own instance connected, the Atlas can read your recordings. On the desktop, a Dawarich panel sits at the bottom of the map, left of the statistics, with two buttons: **Wishlist** and **Countries**. On a phone, a **Dawarich** button sits next to **Bucket List**. Both open the same dialog.
- **Wishlist** checks your bucket list against the stays Dawarich recorded. A wish counts as reached within 250 m and after 20 minutes on the spot, and one wish belongs to exactly one stay. **Tick off _n_** marks the selected wishes visited on the day of the stay. On the desktop bucket list such a wish shows that date with the tooltip *Ticked off from your Dawarich recordings*; click the date to undo it.
- **Countries** lists the countries and cities your recordings cover that are not yet marked visited, with a flag and a city count. **Add _n_ countries** marks them visited, recorded as coming from Dawarich. A country you marked by hand keeps saying it was marked by hand.
Nothing reaches your Atlas until you confirm it. See [Dawarich](Dawarich) for the connection and the full rules.
## Statistics
Your Atlas statistics panel shows:
@@ -71,3 +80,4 @@ Installed plugins can tint countries on the Atlas map with their own layers —
- [Addons-Overview](Addons-Overview)
- [Admin-Addons](Admin-Addons)
- [Dawarich](Dawarich)
+2
View File
@@ -77,7 +77,9 @@ A request for an account that can actually be reset writes **two** rows: one wit
| `admin.places_autocomplete` | Places autocomplete feature toggled |
| `admin.places_details` | Places details feature toggled |
| `admin.places_enrich` | Place enrichment feature toggled |
| `admin.place_shadow` | Place Search Log toggled (`enabled` in details) |
| `admin.collab_features` | Collaboration features updated |
| `admin.transit_provider` | Public transit backend changed (`provider` in details: `transitous` or `google`) |
| `admin.packing_template_create` | Packing template created |
| `admin.packing_template_delete` | Packing template deleted |
| `admin.plugin_retrust` | Plugin signing key re-trusted (records the old and the new key fingerprint) |
+9
View File
@@ -54,6 +54,7 @@ Click **Add expense**, or the pencil beside a row, to open the expense editor:
| Who paid? | Who actually put the money down — see [Who paid](#who-paid). |
| Split | How the total is shared out — see [Splitting costs](#splitting-costs). |
| Note | Free-text note, shown on the row. |
| Receipts & Invoices | Images and PDFs attached to the expense, several per expense. See [Receipts and invoices](#receipts-and-invoices). |
### Expenses linked to a booking or a place
@@ -61,6 +62,14 @@ An expense can hang off a **booking** (reservation or transport) or off a **plac
Deleting the booking or the place deletes its linked expense with it. Removing the expense from the record's Costs block deletes only the expense and leaves the record standing.
### Receipts and invoices
An expense can carry the receipt or invoice behind it. **Attach receipt / invoice** in the expense editor opens the file picker for images and PDFs, and several files can go on one expense. They are uploaded when the expense is saved, land in the trip's Files with a link to the expense, and are listed in the editor's **Receipts & Invoices** block from then on. If the save fails after the upload, the uploaded files are taken back out again; any that could not be removed are reported, so you can delete them in the Files tab.
A row with receipts shows a **Receipts** chip beside the name, with the count when there is more than one. Click it to open the viewer: it shows one receipt at a time, pages through them with the **arrow keys**, and offers a download for a PDF.
**Remove receipt** in the editor only unlinks the file from the expense. The file itself stays on the trip, because editing an expense (`budget_edit`) does not carry the file permission: to get rid of the file, delete it in the Files tab, which needs `file_delete`. Uploading a receipt goes through the trip's file upload, so it needs `file_upload` on top of `budget_edit`. A file that is also linked to a place or a booking keeps those links. See [Documents-and-Files](Documents-and-Files).
## Who paid
**Who paid?** in the expense editor records who actually put the money down. It is the other half of the settlement maths — the split says who owes for the expense, this says who is out of pocket for it:
+4
View File
@@ -16,6 +16,10 @@ Type in the input field at the bottom and press **Enter** (or click the send but
Messages load in pages of 100. A **Load older messages** button appears at the top of the chat when older messages are available.
### Images
A message can carry up to **four images**. Click the image button in the composer, paste them from the clipboard, or drop them onto the composer. JPEG, PNG, GIF and WebP are accepted, up to **10 MB** each; TREK checks the file type and the extension, and anything else is refused with *Only JPEG, PNG, GIF and WebP images up to 10 MB are allowed*. Each picked image shows as a thumbnail above the input with an **×** to drop it again, and a message can consist of images alone, with no text. Sending an image needs the `file_upload` permission on top of `collab_edit`.
## Emoji
Click the smiley-face button in the composer to open the emoji picker. The picker has three categories:
+1 -1
View File
@@ -110,7 +110,7 @@ Sharing works between people on the same TREK. To give a list to somebody who ru
- **New list** is the default: the file becomes a list of your own, under a name you can change right there.
- **Add to a list** puts the places into a list you already have. The list is picked from the ones you may edit, which is your own lists plus a shared one where you are an editor or an admin; the list you have open is preselected.
Adding to a list only ever adds. A place the list already has is left exactly as it is, with its status, notes, rating and labels, and counted as already there when the import reports what it did. TREK recognises it the same way it does when you save a single place: by the provider it came from, then by name, then by position. The list keeps its own name, colour, icon and description, whatever the file says about the list it came from; a label the file brings that the list does not have is created, and one it already has keeps its name and colour.
Adding to a list only ever adds. A place the list already has is left exactly as it is, with its status, notes, rating and labels, and counted as already there when the import reports what it did. TREK recognises it the same way it does when you save a single place: by the provider it came from, then by name. Position only counts for a place without a name, which a file never carries, so a place that was renamed since the export is added a second time unless a provider id still identifies it. The list keeps its own name, colour, icon and description, whatever the file says about the list it came from; a label the file brings that the list does not have is created, and one it already has keeps its name and colour.
From a GPX, every waypoint becomes a place, and so does a route point somebody named. A track is a line rather than a place, so it is not imported, and the preview says how many track points were left behind. A waypoint's type (OsmAnd files its favourites into groups this way) becomes the place's category when your palette has one of that name. A waypoint without a name is called "Waypoint" and its number in the file; one without usable coordinates is skipped and counted.
+2 -1
View File
@@ -29,7 +29,7 @@ The dashboard stays reachable at `/dashboard` — only the root redirects.
Shown once **Active trip** is selected: which tab of the trip planner to open on. Handy when you mostly reach for one thing on the road, for example entering expenses on the Costs tab.
If the tab you picked belongs to an addon that is switched off, the trip opens on **Plan** instead.
If the tab you picked belongs to an addon that is switched off, the trip opens on **Plan** instead. The same happens with **Road trip** on the desktop: that tab exists only on the phone, and the desktop reaches the drive through the **Days** / **Road trip** switch on **Plan**.
### Linking to a tab directly
@@ -44,6 +44,7 @@ The parameter takes the planner's internal tab ids, which are historic German na
| Tab | Id |
|-----|-----|
| Plan | `plan` |
| Road trip | `roadtrip` (phone only; on the desktop the trip opens on Plan, where the **Days** / **Road trip** switch sits) |
| Transports | `transports` |
| Bookings | `buchungen` |
| Lists | `listen` |
+1 -1
View File
@@ -8,7 +8,7 @@ Attach and manage documents, tickets, and other files for your trip.
Open the **Files** tab inside the trip planner, or navigate directly to `/trips/:id/files`.
> **Admin:** Files is an addon. Enable it in [Admin-Addons](Admin-Addons).
> **Admin:** The Files tab belongs to the **Documents** addon. Enable it in [Admin-Addons](Admin-Addons).
## Uploading
+12 -4
View File
@@ -6,12 +6,17 @@ TREK encrypts sensitive settings at rest using AES-256-GCM. The following values
- Google Maps API key (instance-wide, in `app_settings`; the per-user column is still read as a fallback)
- Unsplash access key (instance-wide, in `app_settings`; the per-user column is still read as a fallback)
- Amap API key (instance-wide, in `app_settings`; the per-user column is still read as a fallback)
- Mapbox access token (per user)
- CARTO API key (per user, in `settings` table)
- OpenWeather API key (per user)
- Immich API key (per user)
- AirTrail API key (per user)
- Dawarich API key (per user, in `dawarich_connections`)
- Synology Photos password, session ID, and device ID (per user)
- Per-user webhook URL, ntfy notification token, and LLM provider API key (in `settings` table)
- Instance-wide API key of the AI Parsing addon (in `addons.config`)
- Document sync credentials of a trip's stores, one encrypted blob per store and trip (in `document_connections`), and the webhook secret of each binding (in `trip_document_links`); see [Document-Sync](Document-Sync)
- OIDC client secret (global, in `app_settings`)
- SMTP password (global, in `app_settings`)
- Admin webhook URL and admin ntfy token (global, in `app_settings`)
@@ -46,7 +51,7 @@ To find your current key: check the `ENCRYPTION_KEY` environment variable or rea
## Rotating the key
Use `scripts/migrate-encryption.ts` to re-encrypt all stored secrets without downtime or manual re-entry.
Use `scripts/migrate-encryption.ts` to re-encrypt the stored secrets listed below without downtime or manual re-entry.
**Docker:**
@@ -66,16 +71,19 @@ The script:
2. Asks for confirmation before making any changes.
3. Creates a timestamped backup of the database (e.g. `travel.db.backup-1713484800000`) before modifying anything.
4. Re-encrypts all stored secrets across all tables:
- `app_settings`: `oidc_client_secret`, `smtp_pass`, `admin_webhook_url`, `admin_ntfy_token`, `maps_api_key`, `unsplash_api_key`
- `app_settings`: `oidc_client_secret`, `smtp_pass`, `admin_webhook_url`, `admin_ntfy_token`, `maps_api_key`, `unsplash_api_key`, `amap_api_key`
- `app_settings['storage.backends']`: the `secretAccessKey` of every S3 storage backend
- `users` (per user): `maps_api_key`, `unsplash_api_key`, `openweather_api_key`, `immich_api_key`, `synology_password`, `synology_sid`, `synology_did`, `airtrail_api_key`, `mfa_secret`
- `settings` (per user): `webhook_url`, `ntfy_token`, `mapbox_access_token`, `llm_api_key`
- `users` (per user): `maps_api_key`, `unsplash_api_key`, `amap_api_key`, `openweather_api_key`, `immich_api_key`, `synology_password`, `synology_sid`, `synology_did`, `airtrail_api_key`, `mfa_secret`
- `settings` (per user): `webhook_url`, `ntfy_token`, `mapbox_access_token`, `carto_api_key`, `llm_api_key`
- `dawarich_connections` (per user): `api_key`
- `plugin_oauth_tokens`: `access_token`, `refresh_token` (per plugin and user)
- `plugins.config` and `plugin_user_config.config`: every settings field a plugin's manifest marks `secret`, resolved per plugin and scope from `plugin_settings_fields`
- `trip_album_links`: `passphrase`
- `trek_photos`: `passphrase`
5. Reports counts of migrated, already-migrated, skipped (empty), and errored values.
**Not covered by the script:** the document sync credentials (`document_connections.secrets` and `trip_document_links.webhook_secret`) and the instance-wide API key of the AI Parsing addon (`addons.config`). They stay encrypted under the old key and read back as empty afterwards. The AI Parsing key has to be entered again under **Admin → Addons**. A document sync binding fails with *The credentials were refused.* and cannot be repaired from the trip: the connection form only opens for a store the trip has not been connected to yet, and **Disconnect** removes the binding but keeps the stored connection. Removing the trip's row from `document_connections` takes its bindings with it and lets the trip owner connect the store again; the documents themselves stay in TREK and at the store. Rotate before trips are bound to a store where you can. See [Document-Sync](Document-Sync).
After a successful migration:
1. Update `ENCRYPTION_KEY` in your environment to the new value.
+9 -6
View File
@@ -7,7 +7,7 @@ Complete reference for all environment variables TREK reads.
- **Docker Compose** — use the `environment:` block or a `.env` file alongside `docker-compose.yml`
- **Docker run** — pass each variable with `-e VARIABLE=value`
- **Helm** — use `env:` for plain values and `secretEnv:` for sensitive values in `values.yaml`. The chart only
passes through the keys it declares (26 in `templates/configmap.yaml`, 5 in `templates/secret.yaml`), so a variable
passes through the keys it declares (28 in `templates/configmap.yaml`, 5 in `templates/secret.yaml`), so a variable
that is not one of them is dropped silently — patch it onto the Deployment or add it to the chart
- **Unraid** — set in the container template editor
- **Proxmox Community Script** — set in `/opt/trek/server/.env`
@@ -51,7 +51,7 @@ like `NODE_ENV=staging` still boots.
| `LOG_LEVEL` | `info` = concise user actions; `debug` = verbose details | `info` |
| `DEFAULT_LANGUAGE` | Default language on the login page — see supported codes below | `en` |
| `SESSION_DURATION` | How long a login session stays valid before re-login is required. Used when **"Remember me" is unchecked** on the login form (the default): applies to the `trek_session` JWT `exp` claim, and the cookie is issued as a **browser-session cookie** (no `maxAge`, cleared when the browser closes). Accepts `ms`-style strings: `1h`, `12h`, `7d`, `30d`, `90d`. Invalid values abort startup — see Startup Validation above. Does not affect the short-lived MFA challenge token or MCP OAuth tokens (those keep their own TTL). | `24h` |
| `SESSION_DURATION_REMEMBER` | Session length used when the user **ticks "Remember me"** on login: a longer-lived JWT `exp` claim plus a **persistent** `trek_session` cookie whose `maxAge` matches, so the session survives browser restarts. Same `ms`-style format and the same startup validation as `SESSION_DURATION`. | `30d` |
| `SESSION_DURATION_REMEMBER` | Session length used when the user **ticks "Remember me"** on login: a longer-lived JWT `exp` claim plus a **persistent** `trek_session` cookie whose `maxAge` matches, so the session survives browser restarts. Same `ms`-style format and the same startup validation as `SESSION_DURATION`. Also the lifetime of every SSO session when `OIDC_ONLY=true`, where the login page has no such switch. | `30d` |
| `ALLOWED_ORIGINS` | Comma-separated origins for CORS and email notification links | same-origin |
| `ALLOW_INTERNAL_NETWORK` | Allow outbound requests to private/RFC-1918 IPs. Set `true` if Immich, a document store bound through [Document-Sync](Document-Sync) or other integrated services are on your local network. Loopback (`127.x`) and link-local (`169.254.x`) addresses remain blocked regardless, apart from a link-local address listed in `ALLOW_LINK_LOCAL_IPS`. | `false` |
| `ALLOW_LINK_LOCAL_IPS` | Single link-local addresses (`169.254.x.x`) TREK may reach after all, comma-separated. Meant for the host gateway of a rootless Podman container, `169.254.1.2`, with an identity provider or another service behind it. OIDC, AI Parsing, plugin OAuth and your own routing engines reach a listed address directly; the other integrations treat it as internal, so they also need `ALLOW_INTERNAL_NETWORK=true`. `169.254.169.x` and `169.254.170.x`, where cloud providers serve metadata and credentials, cannot be listed, and an entry that is not a single link-local IPv4 aborts startup. See [Internal-Network-Access](Internal-Network-Access#a-link-local-address-you-need). | unset |
@@ -142,8 +142,9 @@ proxying is disabled by default.
> handled by TREK's SSRF protection use a dedicated dispatcher and do not use the environment proxy.
> **Container only.** Node ignores these variables unless it is started with `NODE_USE_ENV_PROXY=1`, and the official
> image sets that for you. On a source or Proxmox install, and on Helm where the chart's ConfigMap only passes through
> the keys it knows, set `NODE_USE_ENV_PROXY=1` alongside them or nothing will change.
> image sets that for you. On a source or Proxmox install, set `NODE_USE_ENV_PROXY=1` alongside them or nothing will
> change. On Helm the image already has it, but the chart's ConfigMap does not declare `HTTP_PROXY`, `HTTPS_PROXY` or
> `NO_PROXY`, so a value under `env:` is dropped; patch the three onto the Deployment instead.
> **Set `NO_PROXY`.** Without it every request goes to the proxy, including the ones TREK makes to itself, such as the
> container health check. `localhost,127.0.0.1` is a sensible minimum; add your own hosts as needed.
@@ -181,7 +182,7 @@ For setup instructions, see [OIDC-SSO](OIDC-SSO).
| `OIDC_CLIENT_ID` | OIDC client ID | — |
| `OIDC_CLIENT_SECRET` | OIDC client secret | — |
| `OIDC_DISPLAY_NAME` | Label shown on the SSO login button | `SSO` |
| `OIDC_ONLY` | Force SSO-only mode: disables password login and registration, overrides Admin > Settings toggles, cannot be changed at runtime. First SSO login becomes admin on a fresh instance. | `false` |
| `OIDC_ONLY` | Force SSO-only mode: disables password login and registration, overrides Admin > Settings toggles, cannot be changed at runtime. First SSO login becomes admin on a fresh instance. Since the login page then has no "Remember me" switch, every SSO session gets the `SESSION_DURATION_REMEMBER` lifetime; shorten that variable for shorter sessions, `SESSION_DURATION` does not apply here. | `false` |
| `OIDC_ADMIN_CLAIM` | OIDC claim inspected for the admin role. Only takes effect once `OIDC_ADMIN_VALUE` is set. | `groups` |
| `OIDC_ADMIN_VALUE` | Value of the OIDC claim that grants admin role (e.g. `app-trek-admins`) | — |
| `OIDC_SCOPE` | Space-separated OIDC scopes to request. **Fully replaces** the default — always include `openid email profile` plus any extra scopes (e.g. add `groups` when using `OIDC_ADMIN_CLAIM`) | `openid email profile` |
@@ -288,6 +289,8 @@ Booking import can also fall back to an AI model for documents KDE Itinerary can
Public-transit routing in the planner is powered by [Transitous](https://transitous.org/), a free community MOTIS service — no API key is required. See [Transport: Flights, Trains, Cars](Transport-Flights-Trains-Cars) for the feature itself.
An admin can switch the backend under **Admin → Settings → API Keys → Transit Provider**: **Transitous (free)** is the default and the fallback, **Google** answers with the Google API key set on the same card and is billed per search. While no Google key is set, searches keep going to Transitous. `TRANSIT_API_URL` below only applies to the Transitous path.
| Variable | Description | Default |
|-------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------|
| `TRANSIT_API_URL` | Base URL of the transit routing API. TREK's server proxies requests to it. Point this at your own self-hosted [MOTIS](https://github.com/motis-project/motis) instance if you want zero third-party egress. A trailing slash is stripped. | `https://api.transitous.org` |
@@ -411,7 +414,7 @@ next key rotation or admin reset would quietly switch the file back to WAL.
|---------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------|
| `IDEMPOTENCY_TTL_SECONDS` | How long (in seconds) stored idempotency keys are kept before garbage collection. The offline client replays queued mutations with their `X-Idempotency-Key` on reconnect, so this must exceed the longest expected offline window or a replay could create a duplicate. Invalid values abort startup. | `2592000` (30 days) |
| `NOMINATIM_URL` | Custom HTTP(S) Nominatim base URL for every geocoding call the server makes: place search, reverse lookup, place details and the Atlas region fill. When set it **replaces** the public OpenStreetMap service for all of them, with no fallback, so point it at an instance whose import covers the places your users plan and that speaks the Nominatim API (`/search`, `/reverse`, `/lookup`). Photon, Geoapify and LocationIQ are not drop-in replacements. TREK keeps its client-side spacing of roughly one request a second whichever instance answers, so this changes where the requests go, not how fast they are sent. Unset or blank uses the default. With the TREK Places API on, suggestions while typing come from the TREK API's own OpenStreetMap layer and reach Nominatim only when the TREK API has nothing. | `https://nominatim.openstreetmap.org` |
| `OVERPASS_URL` | Custom [Overpass API](https://wiki.openstreetmap.org/wiki/Overpass_API) endpoint(s), comma-separated, for the map's category buttons, the Road trip search along the drive and the `search_pois` MCP tool whenever the TREK Places API has no answer or is switched off. When set it **replaces** the bundled public mirrors: point it at an internal or self-hosted Overpass instance when the public mirrors are unreachable from your network (e.g. firewalled/locked-down egress in a Kubernetes cluster). Entries that aren't valid `http(s)` URLs are ignored. If you don't run your own Overpass but the public mirrors throttle TREK, first make sure `APP_URL` (or `ALLOWED_ORIGINS`) is set: that alone gives outbound Overpass/Nominatim requests a unique User-Agent, which the public mirrors rate-limit far less. | bundled public mirrors |
| `OVERPASS_URL` | Custom [Overpass API](https://wiki.openstreetmap.org/wiki/Overpass_API) endpoint(s), comma-separated, for the map's category buttons, the Road trip search along the drive and the `search_pois` MCP tool whenever the TREK Places API has no answer or is switched off. When set it **replaces** the bundled public mirrors: point it at an internal or self-hosted Overpass instance when the public mirrors are unreachable from your network (e.g. firewalled/locked-down egress in a Kubernetes cluster). Entries that aren't valid `http(s)` URLs are ignored. If you don't run your own Overpass but the public mirrors throttle TREK, first make sure `APP_URL` (or `ALLOWED_ORIGINS`) is set: that alone gives outbound Overpass/Nominatim requests a unique User-Agent, which the public mirrors rate-limit far less. One request is not covered: the tag lookup behind an OpenStreetMap place's details (website, phone, Wikidata) still goes to `overpass-api.de` whatever this is set to, and where that host is unreachable those details fall back to the tags Nominatim returns. | bundled public mirrors |
| `OVERPASS_TIMEOUT_MS` | Per-endpoint timeout (in milliseconds) for Overpass POI requests. Endpoints race in parallel and one that hasn't answered within this window is abandoned so a faster mirror can win. The query itself gives Overpass 20 seconds of work, so a lower value hangs up on answers that were still coming; that is why the default rose from `12000` in 4.3.0. Drop any lower value you set by hand, and raise it only for a slow self-hosted Overpass instance. Invalid values abort startup. | `25000` |
| `LLM_TIMEOUT_MS` | How long (in milliseconds) one AI-parsing call may take before it is abandoned. One ceiling for every provider, applied to the abort signal and to the underlying HTTP client alike. The default is generous so heavier parsing work fits without a code change; lower it if you use a cloud provider and would rather fail fast. Invalid values abort startup. | `900000` (15 min) |
+2 -2
View File
@@ -59,13 +59,13 @@ TREK is a self-hosted, real-time collaborative travel planner licensed under AGP
- **MCP Server**: built-in Model Context Protocol server with OAuth 2.1 authentication
- **150+ Tools**: create trips, plan itineraries, manage budgets, send messages, and more
- **30 Resources**: read-only `trek://` URIs for trips, days, places, budget, packing, journeys, and more
- **29 OAuth Scopes**: granular permissions across 14 permission groups
- **35 OAuth Scopes**: granular permissions across 17 permission groups
- **Pre-built Prompts**: `trip-summary`, `packing-list`, and `budget-overview` context loaders
### Admin
- User management, invite links, packing templates, global categories
- Addon management, API key storage, scheduled auto-backups
- System notices for onboarding and announcements
- System notices for onboarding and announcements, including a release notes notice every user sees once after each update (see [Admin-GitHub-Releases](Admin-GitHub-Releases#release-notes-notice))
> **Admin:** Most configuration lives in the Admin Panel. On first boot TREK seeds an admin account automatically: credentials come from `ADMIN_EMAIL` / `ADMIN_PASSWORD` if set, otherwise a random password is printed to the container log.
+2 -2
View File
@@ -39,8 +39,8 @@ See [Environment-Variables](Environment-Variables) for the full list.
| Tag | Example | Behavior |
|---|--------------------------|---|
| `latest` | `mauriceboe/trek:latest` | Always the newest release across all major versions |
| Major version | `mauriceboe/trek:3` | Latest release pinned to that major version |
| Full version | `mauriceboe/trek:3.4.0` | Exact release; never changes |
| Major version | `mauriceboe/trek:4` | Latest release pinned to that major version |
| Full version | `mauriceboe/trek:4.0.0` | Exact release; never changes |
Replace `mauriceboe/trek:latest` in the run command with your chosen tag to pin to a major version or exact release.
+4 -3
View File
@@ -110,10 +110,9 @@ env:
# TZ: "Europe/Berlin" # timezone for logs, reminders, cron jobs
# LOG_LEVEL: "info" # "info" = concise, "debug" = verbose
# TREK_WIKI_DIR: "/app/wiki" # where /help reads its docs from; leave unset (the image ships them)
# DEFAULT_LANGUAGE: "en" # fallback language on login page; supported: de, en, es, fr, hu, nl, br, cs, pl, ru, zh, zh-TW, it, tr, ar, id, ja, ko, uk, gr, sv, vi, ca
# ALLOWED_ORIGINS: "https://trek.example.com"
# APP_URL: "https://trek.example.com"
# FORCE_HTTPS: "false" # enable HTTPS redirect + HSTS; requires TRUST_PROXY
# FORCE_HTTPS: "false" # enable HTTPS redirect + HSTS; set TRUST_PROXY for correct client IPs
# TRUST_PROXY: "1" # proxy hops for X-Forwarded-For/Proto; defaults to 1 in production
# COOKIE_SECURE: "true" # auto-derived; set "false" only for local HTTP testing
# ALLOW_INTERNAL_NETWORK: "false" # set "true" if Immich or other services are on a private network
@@ -129,6 +128,8 @@ env:
# OIDC_DISCOVERY_URL: "" # override for providers with non-standard discovery paths (e.g. Authentik)
```
> **Note:** `DEFAULT_LANGUAGE` is not declared in the chart's ConfigMap, so a value under `env:` is dropped and the fallback stays `en`. Patch it onto the Deployment if you need it. See [Environment-Variables](Environment-Variables#default_language--supported-codes) for the codes.
### Sensitive Variables (`secretEnv`)
These are stored in a Kubernetes Secret and injected as environment variables:
@@ -179,7 +180,7 @@ helm install trek trek/trek --set probes.liveness.initialDelaySeconds=60 --s
Raise `initialDelaySeconds` on slow storage or after a large migration, where the first start can outlast the default and leave the pod restarting in a loop. Overrides are merged over the defaults, so setting one key keeps the rest. To switch a probe to `exec` or `tcpSocket`, clear the shipped handler in the same override (`--set probes.liveness.httpGet=null`), otherwise Kubernetes rejects the pod with "may not specify more than 1 handler type". Set `probes.liveness=null` to drop a probe entirely.
If you change `env.PORT`, update `service.port` and `probes.*.httpGet.port` to match.
Leave `env.PORT` at `3000`. The chart passes it to the server, but `containerPort` in `deployment.yaml` and `targetPort` in `service.yaml` are fixed at 3000, so a different value makes the server listen where nothing routes to it. Changing it means patching both templates as well as `service.port` and `probes.*.httpGet.port`; `service.port` alone is not enough.
### Ingress
+2 -2
View File
@@ -20,10 +20,10 @@ Under the strict guard, these ranges are blocked whatever `ALLOW_INTERNAL_NETWOR
|---|---|
| `127.0.0.0/8`, `::1` | Loopback |
| `0.0.0.0/8` | Unspecified |
| `169.254.0.0/16`, `fe80::/16` | Link-local / cloud metadata endpoints |
| `169.254.0.0/16`, `fe80::/10` | Link-local / cloud metadata endpoints |
| `::ffff:127.x.x.x`, `::ffff:169.254.x.x` | IPv4-mapped loopback and link-local |
The IPv6 link-local rule here matches the `fe80:` hextet only, which is narrower than the nominal `fe80::/10` prefix (`fe80:` to `febf:`). In practice that is the same set of addresses, since RFC 4291 link-local addresses are always `fe80::/64`. The relaxed guard covers the whole `/10`.
The IPv6 link-local rule covers the whole `fe80::/10` prefix (`fe80:` to `febf:`), under the relaxed guard as well.
The one way past this table is `ALLOW_LINK_LOCAL_IPS`, for a single IPv4 link-local address, see [below](#a-link-local-address-you-need).
+18 -2
View File
@@ -18,6 +18,8 @@ When the admin has enabled the Journey addon, a **Journey** entry appears in the
From the Journey list, click **Create journey**. Give it a title and optional subtitle, then select one or more existing trips to link. Linking a trip imports the trip's places as location anchors for your entries — one anchor per day the place stands on, so a place you kept across two days gives you an entry on each of them. You can link additional trips later from the journal settings.
A place brought in from a linked trip sits in the timeline as a **suggestion** until you write into it. One you will not use can be dismissed on its own, with **Dismiss this suggestion** on its card (on a phone, in its sheet). A dismissed suggestion leaves the journey without being deleted, and the trip sync does not offer the same place again. **Journey Settings** shows how many you dismissed and **Bring back dismissed suggestions** returns them all.
## Journal entries
Each entry corresponds to a day in your journey. The entry editor provides:
@@ -34,17 +36,27 @@ Each entry corresponds to a day in your journey. The entry editor provides:
| `rough` | Rough | Violet |
- **Weather** — choose one of six values: Sunny, Partly cloudy, Cloudy, Rainy, Stormy, Snowy. (Snowy is stored under the id `cold`.)
- **Photos** — attach photos to the entry. The first photo becomes the card thumbnail in list views.
- **Photos and videos** — attach photos to the entry. The first photo becomes the card thumbnail in list views. A video clip goes on an entry the same way: `mp4`, `m4v`, `webm` or `mov`, up to 500 MB per file. The clip is stored as uploaded, without transcoding, and the editor shows its first frame as a preview before you save. The journey gallery takes the same clips.
> **Note on HEIC files:** HEIC is an Apple-only format that many browsers and platforms do not recognise as an image. To ensure broad compatibility, HEIC/HEIF files are automatically converted to JPEG before upload. This conversion may result in the loss of embedded metadata (EXIF data such as GPS coordinates, camera information, etc.).
- **Pros / Cons** — optional verdict cards. Add items to a **Pros** list (thumbs-up) or a **Cons** list (thumbs-down) to summarise what you loved or what could have been better. These are stored in the `pros_cons.pros` and `pros_cons.cons` arrays on the entry.
- **Tags** — free-form labels (e.g. "hidden gem", "best meal").
- **Location** — pin the entry to a map location.
- **Time** — optionally record a time of day for the entry.
### Entry fields
Mood, Weather and Pros / Cons can be switched off for a journey that does not use them. Open **Journey Settings** and use the switches under **Entry fields**: **Mood**, **Weather** and **Pros & cons**. Nothing already written is lost: the form stops asking, the stored values stay, and switching a field back on brings them into view. A shared journey hides the fields its owner switched off as well.
### Getting around a long journal
Everything that adds to a journal sits at the top of the page — the **Add Entry** button, the gallery upload — while what you were last reading sits at the bottom. On desktop, two small round buttons float centred over the timeline, just above its bottom edge, once there is more than 400 px of scroll to travel: one jumps back to the top, the other to the last entry. Each appears only when there is somewhere to go in that direction.
A journey that is still under way opens on today rather than at its first entry, as long as today falls inside it; a day with nothing written lands you on the last entry before it. A journey that has ended opens at the beginning.
On desktop, the **Search this journey** box in the toolbar filters the timeline as you type, across the titles, stories, places and tags of the entries. Matching ignores case and accents, so `cafe` finds `Café`.
Each day header carries a **+** button, **Add an entry on this day**, which opens the editor with that date already set, so an entry for an earlier day no longer starts on today.
### External photos
The entry editor includes an **External photos** tab for connected Immich and Synology Photos libraries. It searches the selected calendar day automatically. When the entry has a pinned location, photos with GPS metadata are shown nearest to that location first. Photos without GPS, and photos taken farther away on the same day, remain available below the nearby results; no distance cutoff is applied.
@@ -53,7 +65,11 @@ External selections are queued with the other editor changes and saved only when
## Mobile timeline
On mobile, entries are displayed in a horizontal scrolling timeline of card thumbnails. Tap a card to open the full entry view in a modal sheet. Each card shows the entry's first photo (or a placeholder pin), date, day number, mood icon, and weather icon.
On a phone, entries sit in a horizontal carousel over the map, one photo card per entry. The entry's first photo is the card (a pin placeholder when it has none), with the title and place on it, the country flag and the date in the corners, and the mood and weather icons next to the date. A suggestion from a linked trip shows a plus mark and its title in italics. The day an entry belongs to is carried by the colour of the card's edge, the same colour its map marker uses; there is no day number on the card. Tap the centred card to open the entry in a sheet.
Above the cards, once the journey spans more than one day, a day bar shows one segment per day in the day's colour: it tells you how long the journey is and how far in you are, and tapping a segment, or pressing the date above it and sliding sideways, jumps to that day.
The **+** in the bottom bar starts a quick capture for a moment on the move: a title, a picture (**Photo** takes one with the camera, **Gallery** picks from your phone) and a short note. Your current position is filled in as the entry's location, the place name is looked up from it, and the current weather is set when it can be. **+ Details** opens the full form. From the header of an entry's sheet, the two arrows move the entry earlier or later within its day; suggestions do not take part.
## Map view
+30 -6
View File
@@ -1,6 +1,6 @@
# MCP Addon Tools and Resources
This page covers MCP tools and resources that require specific addons to be enabled on your TREK instance. For the rest of the surface (trips, places, day planning, accommodations, transport, reservations, tags, maps, and notifications — plus the Budget tools, which need the Budget addon but are documented there) see [MCP-Tools-and-Resources](MCP-Tools-and-Resources).
This page covers MCP tools and resources that require specific addons to be enabled on your TREK instance. For the rest of the surface (trips, places, day planning, accommodations, transport, reservations, tags, maps, and notifications — plus the Budget tools, which need the Budget addon, listed as **Costs** under **Admin → Addons**, but are documented there) see [MCP-Tools-and-Resources](MCP-Tools-and-Resources).
---
@@ -8,12 +8,13 @@ This page covers MCP tools and resources that require specific addons to be enab
### Packing _(Packing addon required)_
Requires `packing:read` or `packing:write` scope.
Requires `packing:read` or `packing:write` scope. The Packing addon is listed as **Lists** under **Admin → Addons** (id `packing`) and covers packing lists and to-dos.
| Tool | Description |
|---|---|
| `create_packing_item` | Add an item to the packing checklist with optional category. |
| `update_packing_item` | Rename an item or change its category. |
| `set_packing_item_sharing` | Move an item between the three sharing tiers: `common` puts it in the pool the whole trip packs from, `personal` keeps it to its owner, `shared` covers the trip members in `recipient_ids`. Only the item's owner may change this. |
| `toggle_packing_item` | Check or uncheck a packing item. |
| `delete_packing_item` | Remove a packing item. |
| `reorder_packing_items` | Set the display order of packing items within a trip. |
@@ -55,6 +56,7 @@ Requires `atlas:read` or `atlas:write` scope.
| `unmark_country_visited` | Remove a country from your visited list. |
| `get_atlas_stats` | Get atlas statistics — visited country counts, region counts, and continent breakdown. |
| `list_visited_regions` | List all manually visited sub-country regions for the current user. |
| `locate_atlas_region` | Resolve a coordinate to the country and region codes the Atlas map can highlight, which is where the pair `mark_region_visited` expects comes from. A point outside every bundled polygon answers with `null` fields rather than an error. For a postal address use `reverse_geocode` instead. |
| `mark_region_visited` | Mark a sub-country region as visited (e.g. `"US-CA"`). |
| `unmark_region_visited` | Remove a region from the visited list. |
| `get_country_atlas_places` | Get places saved in the user's atlas for a specific country. |
@@ -66,7 +68,7 @@ Requires `atlas:read` or `atlas:write` scope.
Requires `collab:read` or `collab:write` scope.
The addon alone is not enough. Collab has four sub-features an admin switches on and off independently in Admin → Addons — Notes, Polls, Chat and What's Next, all on by default — and three of them gate MCP entries. A tool or resource is only registered when its own sub-feature is enabled on top of the addon, so switching one off makes its entries vanish from the tool list rather than return an error. What's Next has no MCP tools or resources of its own, so toggling it changes nothing here.
The addon alone is not enough. Collab has five sub-features an admin switches on and off independently in Admin → Addons — Notes, Polls, Chat, Links and What's Next, all on by default — and three of them gate MCP entries. A tool or resource is only registered when its own sub-feature is enabled on top of the addon, so switching one off makes its entries vanish from the tool list rather than return an error. Links and What's Next have no MCP tools or resources of their own, so toggling them changes nothing here.
| Sub-feature | Tools it gates | Resource it gates |
|---|---|---|
@@ -98,6 +100,7 @@ Requires `collections:read` or `collections:write` scope.
| `list_collections` | List the saved-place collections the user owns or has accepted a share for, plus any pending incoming invites. |
| `get_collection` | Get one collection with its members, labels, and all saved places, including the average rating and each member's vote. |
| `available_collection_users` | List users who can still be invited to a collection (excludes current members and guests). |
| `find_place_in_collections` | Answer whether a place is already on one of your lists, across the whole library in one call, naming each list and the status the place has there. Identify the place by `google_place_id` / `google_ftid` from `search_place` or by `lat` + `lng`; a name alone is never matched. |
| `create_collection` | Create a new saved-place collection owned by the user. |
| `update_collection` | Update a collection's name, description, colour, icon, cover, links, or sort order. Owner/admin only. |
| `delete_collection` | Permanently delete a collection and all its saved places. Owner only, and it cannot be undone. |
@@ -106,6 +109,7 @@ Requires `collections:read` or `collections:write` scope.
| `save_trip_places_to_collection` | Copy one or more existing trip places into a collection. Duplicates are skipped unless `force` is true. |
| `update_collection_place` | Update a saved place's name, address, coordinates, description, notes, status, category, links, tags, labels, image, or move it to another collection. |
| `set_collection_place_status` | Set a saved place's status: `idea`, `want`, or `visited`. |
| `set_collection_place_status_from_trip` | Set a status on every saved copy of the given trip places, in every list they are on, for marking places visited after a day out. Ids are trip place ids. Lists you may only read are skipped. Returns how many saved places changed and how many of the trip places were found in at least one list. |
| `rate_collection_place` | Set or clear the current user's 1-5 star rating on a saved place. Every member rates independently; pass `null` to remove the vote. |
| `delete_collection_place` | Remove a saved place from its collection. Requires delete permission on the list. |
| `copy_collection_places_to_trip` | Copy one or more saved places into a trip, ratings included. Requires edit access to the target trip. |
@@ -128,6 +132,7 @@ Requires `vacay:read` or `vacay:write` scope.
| Tool | Description |
|---|---|
| `get_vacay_plan` | Get the current user's active vacation plan. |
| `get_vacay_year_settings` | Read the caller's leave-year window: `calendar` runs January to December, `fiscal` starts on a configured month and day, `anniversary` on the hire date's month and day. Read it before interpreting a year in `get_vacay_stats` or `get_vacay_entries`, which count over that window. |
| `update_vacay_plan` | Update vacation plan settings (weekend blocking, holidays, carry-over). |
| `set_vacay_color` | Set the current user's color in the vacation plan calendar. |
| `get_available_vacay_users` | List users who can be invited to the current vacation plan. |
@@ -144,16 +149,32 @@ Requires `vacay:read` or `vacay:write` scope.
| `toggle_company_holiday` | Toggle a date as a company holiday for the whole plan. |
| `get_vacay_stats` | Get vacation statistics for a specific year (days used, remaining, carried over). |
| `update_vacay_stats` | Update the vacation day allowance for a specific user and year. |
| `add_holiday_calendar` | Add a public holiday calendar (by region code) to the vacation plan. |
| `add_holiday_calendar` | Add a public holiday calendar (by region code from `list_holiday_countries`) to the vacation plan, or a school-holiday calendar with `type: 'school_holiday'` and a region code from `list_school_holiday_regions` or `list_manual_school_holiday_regions`. A school-holiday calendar only shows up once `update_vacay_plan` has set `school_holidays_enabled`. |
| `update_holiday_calendar` | Update label or color for a holiday calendar. |
| `delete_holiday_calendar` | Remove a holiday calendar from the vacation plan. |
| `list_holiday_countries` | List countries available for public holiday calendars. |
| `list_holidays` | List public holidays for a country and year. |
| `list_school_holiday_regions` | List a country's school-holiday regions. Pass `calendar_regions[].region` verbatim as the region of `add_holiday_calendar`; a group is stored as `COUNTRY\|group:CODE`, and a bare group code would leave the calendar empty. |
| `list_school_holidays` | List school holidays for a country and year, narrowed to a subdivision or group code from `list_school_holiday_regions`. These are term breaks; a day off is normally counted against the public holidays from `list_holidays`. |
| `list_vacay_shares` | List read-only calendar shares — who you share your calendar with, and which calendars are shared with you. |
| `get_shareable_vacay_users` | List the users the caller can share their calendar with, for `share_vacay_calendar`. A wider set than `get_available_vacay_users`, which lists candidates for merging plans and so leaves out everyone already in a plan of their own. |
| `share_vacay_calendar` | Share the current user's vacation calendar with another user (view only, no merge). |
| `unshare_vacay_calendar` | Remove a read-only calendar share — revoke one you shared, or remove a calendar shared with you. |
| `get_shared_vacay_calendars` | Get the read-only calendars shared with the current user for a year (entries and company holidays per sharer). |
**Manual school holidays.** The catalog an admin maintains under **Admin → Personalization → School holidays** (see [Vacay](Vacay)) has tools of its own. They are registered whether or not the Vacay addon is on. Reading needs `vacay:read`; every write needs `vacay:write` and an admin account, and a non-admin gets `Admin access required`.
| Tool | Scope | Description |
|---|---|---|
| `list_manual_school_holiday_regions` | `vacay:read` | List the manual catalog: countries with their regions. Pass `regions[].code` verbatim to `add_holiday_calendar` with `type: 'school_holiday'`. Needs no external API. |
| `get_manual_school_holiday_region` | `vacay:read` | Read one region, its current revision and all named holiday periods. Read it before an update, which replaces the whole list of periods and needs the current revision. |
| `list_manual_school_holidays` | `vacay:read` | Named school breaks of a manual region for one year, including breaks that span a year boundary. Both boundary dates are included. |
| `create_manual_school_holiday_country` | `vacay:write`, admin only | Add a country to the catalog with its two-letter uppercase code and display name. Check the catalog first to avoid duplicates. |
| `create_manual_school_holiday_region` | `vacay:write`, admin only | Add a region or school district to an existing country, with named periods as inclusive `YYYY-MM-DD` dates. Use revision `0` on creation. |
| `update_manual_school_holiday_region` | `vacay:write`, admin only | Rename a region and replace its complete list of periods. Supply the current revision; a stale one is rejected. Every calendar using the region follows the change. |
| `delete_manual_school_holiday_region` | `vacay:write`, admin only | Delete a region and its periods, with the current revision. A region a vacation calendar has selected cannot be deleted. |
| `delete_manual_school_holiday_country` | `vacay:write`, admin only | Delete an empty country from the catalog. Remove its unused regions first. |
### Journey _(Journey addon required)_
Requires `journey:read` or `journey:write` scope.
@@ -162,14 +183,16 @@ Requires `journey:read` or `journey:write` scope.
|---|---|
| `list_journeys` | List all journeys owned or contributed to by the current user. |
| `get_journey` | Get a full snapshot of a journey — metadata, entries, contributors, and linked trips. |
| `get_journey_stats` | What a journey adds up to: distance travelled in metres, calendar days spanned, countries in visit order, the furthest point reached, and entry, photo and place counts. Entries switched off with `stats_excluded` count towards none of those and are listed under `excluded`. Pass `include_route` for the route itself, up to 400 stops with coordinates. |
| `create_journey` | Create a new journey with title, optional subtitle, and an initial list of trip IDs. |
| `update_journey` | Update a journey's title, subtitle, or status. |
| `update_journey` | Update a journey's title, subtitle, cover or status, and whether its entries offer a pros/cons list, a mood and a weather note (`show_verdict`, `show_mood`, `show_weather`). Owner only. |
| `restore_journey_suggestions` | Bring back every trip-derived suggestion that was dismissed from a journey. Answers with how many came back. |
| `delete_journey` | Delete a journey. |
| `add_journey_trip` | Link an existing trip to a journey. |
| `remove_journey_trip` | Remove a trip from a journey. |
| `list_journey_entries` | List all entries in a journey (date, text, mood, linked trip). |
| `create_journey_entry` | Add an entry with date (required), optional title, story text, time of day, location name, mood, and sort order. |
| `update_journey_entry` | Edit a journey entry's title, story, date, time of day, or mood. |
| `update_journey_entry` | Edit a journey entry's title, story, date, time of day, place, coordinates, weather, tags, mood, pros/cons list or visibility. `stats_excluded: true` keeps the entry but takes it off the route and out of `get_journey_stats`; `dismissed: true` waves a trip-derived suggestion away without deleting it, so the trip sync does not offer it again. |
| `delete_journey_entry` | Remove an entry from a journey. |
| `reorder_journey_entries` | Reorder entries by providing the new ordered list of entry IDs. |
| `list_journey_contributors` | List the contributors of a journey (owner and editors/viewers). |
@@ -177,6 +200,7 @@ Requires `journey:read` or `journey:write` scope.
| `update_journey_contributor_role` | Change a contributor's role between `editor` and `viewer`. |
| `remove_journey_contributor` | Remove a contributor from a journey. |
| `update_journey_preferences` | Update display preferences for a journey. |
| `add_journey_provider_photos` | Attach photos from a connected library (Immich or Synology Photos) to a journey, or to one entry when `entryId` is given. Find the asset ids first with `search_provider_photos` or `list_provider_album_photos`. No image data passes through: the journey stores a reference and the app fetches the picture. An asset already attached is skipped rather than duplicated. |
| `get_journey_suggestions` | Get suggested trips to add to journeys based on recent trip history. |
| `list_journey_available_trips` | List all trips available to the current user for linking to a journey. |
| `get_journey_share_link` | Get the current public share link for a journey. Requires `journey:share`. |
+1 -1
View File
@@ -56,7 +56,7 @@ TREK defines 35 scopes across 17 groups.
- `list_trips` and `get_trip_summary` are always available regardless of scope — they are navigation tools.
- `plugins:use` grants no data access of its own. It lets a client call tools published by plugins your administrator installed and approved, and each plugin acts with the permissions the administrator granted it — which can reach further than the scopes on your token. The admin's `mcp:tools` grant is the real boundary, not this scope. No client preset selects it for you: a client has to ask for it by name.
- Static tokens and web session JWTs have full access equivalent to all scopes, `plugins:use` included.
- Addon-gated tools (Packing, To-dos, Budget, Collections, Atlas, Collab, Vacay, Journey) require both the relevant scope **and** the corresponding addon to be enabled by an admin. The to-do tools ride the **Packing** addon, not an addon of their own.
- Addon-gated tools (Packing, To-dos, Budget, Collections, Atlas, Collab, Vacay, Journey, Road trip, Dawarich, Document sync and AirTrail) require both the relevant scope **and** the corresponding addon to be enabled by an admin. The to-do tools ride the **Packing** addon (listed as **Lists** under **Admin → Addons**), not an addon of their own. The Road trip tools sit on `trips:*` and `places:write`, the Dawarich tools on `journey:*`, `places:write` and `atlas:write`, the Document sync tools on `files:*` under the **Documents** addon, and the AirTrail tools on `reservations:*`; without the addon, a client holding those scopes does not see them.
## Choosing the right scopes
+18 -8
View File
@@ -20,7 +20,7 @@ Compound tools collapse multi-step workflows into a single atomic transaction. I
| Tool | Wraps | Description |
|---|---|---|
| `create_and_assign_place` | `create_place` + `assign_place_to_day` | Create a place and assign it to a day. Returns `{ place, assignment }`. Requires `places:write`. |
| `create_and_assign_place` | `create_place` + `assign_place_to_day` | Create a place and assign it to a day. Takes the same place fields as `create_place`, `stop_type` included. Returns `{ place, assignment }`. Requires `places:write`. |
| `create_place_accommodation` | `create_place` + `create_accommodation` | Create a place and book it as an accommodation. Returns `{ place, accommodation }`. Requires `trips:write`. |
| `create_budget_item_with_members` | `create_budget_item` + `set_budget_item_members` | Create a budget item and set splitting members. If `userIds` is omitted, behaves like `create_budget_item`. Returns `{ item }`. Requires `budget:write`. |
@@ -33,10 +33,12 @@ Requires `trips:read` or `trips:write` scope.
| `list_trips` | List all trips you own or are a member of. Supports `include_archived` flag. |
| `create_trip` | Create a trip with title, dates, and currency. Days are auto-generated from the date range. |
| `update_trip` | Update a trip's title, description, dates, or currency. |
| `search_cover_images` | Search Unsplash for candidate cover photos and return their URLs, thumbnails and photographer credits. Nothing is saved; pass the chosen photo's URL to `update_trip` as `cover_image`. |
| `delete_trip` | Delete a trip. Owner only. Requires `trips:delete`. |
| `list_trip_members` | List the owner and all collaborators of a trip. |
| `add_trip_member` | Add a user to a trip by username or email. Owner only. |
| `remove_trip_member` | Remove a collaborator from a trip. Owner only. |
| `leave_trip` | Leave a trip you were invited to, giving up your own access. Use this rather than `remove_trip_member` when the user means themselves. The owner cannot leave their own trip. Requires `trips:write`. |
| `create_trip_guest` | Add a travelling companion who has no TREK account. Assignable to budget splits, packing and day participants; never signs in, never emailed. Owner only. |
| `rename_trip_guest` | Rename a guest on a trip. Owner only. |
| `delete_trip_guest` | Delete a guest and re-split the expenses they were part of. Owner only. |
@@ -53,15 +55,19 @@ Requires `places:read` or `places:write` scope.
| Tool | Description |
|---|---|
| `list_places` | List places in a trip, optionally filtered by assignment status, category, tag, or search query. |
| `create_place` | Add a place with name, coordinates, address, category, notes, website, phone, and optional `google_place_id` / `osm_id`. |
| `update_place` | Update any field of an existing place including transport mode, timing, and price. |
| `create_place` | Add a place with name, coordinates, address, category, notes, website, phone, and optional `google_place_id` / `osm_id`. An optional `stop_type` (`fuel`, `charging`, `rest_area`, `campsite`, `restaurant`, `sights` or `hotel`) marks it as a service stop on a drive rather than a destination, so it is left out of the day's stop count; see [Road-Trip](Road-Trip). Leave it unset for an ordinary place. |
| `update_place` | Update any field of an existing place including transport mode, timing, price and `stop_type`. Pass `stop_type: null` to turn a service stop back into an ordinary place. |
| `rate_place` | Set or clear your own 1–5 star rating on a place. Every trip member rates independently and the place shows the average. Pass `null` to clear the vote. |
| `bulk_update_places` | Update many places at once, applying the same field values (e.g. category, price, transport mode) to every listed place in a single call. |
| `bulk_update_places` | Update many places at once, applying the same field values (e.g. category, price, transport mode, `stop_type`) to every listed place in a single call. `stop_type: null` turns the listed service stops back into ordinary places. |
| `delete_place` | Remove a place from a trip. Also removes all day assignments. |
| `bulk_delete_places` | Delete multiple places by ID. Removes all day assignments. Cannot be undone. |
| `import_places_from_url` | Import all places from a publicly shared Google Maps or Naver Maps list URL. |
| `list_categories` | List all available place categories with id, name, icon, and color. |
| `create_category` | Add a category to the instance-wide palette, with name, hex colour and emoji icon. Every trip on the instance sees it, so prefer an existing one from `list_categories`. Admin only; a non-admin gets `Admin access required`. Requires `places:write`. |
| `update_category` | Rename a category or change its colour or icon. Every place already carrying it follows the change. Admin only. Requires `places:write`. |
| `delete_category` | Remove a category from the palette. Places keep their data but lose the category, across every trip. Admin only. Requires `places:write`. |
| `search_place` | Search for a place by name or address, the way the full search in the app does: TREK's own place index and OpenStreetMap together, and Google or Amap only when both are empty. Returns `osm_id` (and `google_place_id` / `google_ftid` when Google answered) for use in `create_place`. Takes an optional `locationBias` to rank results around the trip's destination. |
| `search_places_via_plugins` | Search the place indexes installed plugins provide, alongside `search_place` rather than instead of it. Results have the shape `search_place` returns plus a `rating` and the `pluginId` that found them; only these results can carry a rating. Empty when no plugin offers a search index. Requires `places:read`. |
### Day Planning
@@ -72,12 +78,14 @@ Requires `trips:read` or `trips:write` scope.
| `update_day` | Set or clear a day's title. |
| `create_day` | Add a new day to a trip with optional date and notes. |
| `delete_day` | Delete a day from a trip. |
| `reorder_days` | Reorder whole days by listing every day ID of the trip in the desired order. Each day keeps its places, notes, stays and bookings; on a dated trip the dates stay pinned to their slots, so the content moves across them. For places inside one day use `reorder_day_assignments`. Requires `trips:write`. |
| `set_day_default_transport_mode` | Set the whole-day default travel mode. Per-leg modes still override it. Pass `null` to clear. |
| `assign_place_to_day` | Pin a place to a specific day in the itinerary. Requires `places:write`. |
| `unassign_place` | Remove a place assignment from a day. Requires `places:write`. |
| `reorder_day_assignments` | Reorder places within a day by providing assignment IDs in order. Requires `places:write`. |
| `update_assignment_time` | Set start/end times for a place assignment (e.g. `"09:00"` – `"11:30"`). Pass `null` to clear. Requires `places:write`. |
| `move_assignment` | Move a place assignment to a different day. Requires `places:write`. |
| `update_assignment_notes` | Set or clear the day-specific note on a place assignment, the one `assign_place_to_day` and `create_and_assign_place` accept at creation. Pass `null` or an empty string to clear it. Requires `places:write`. |
| `set_leg_transport_mode` | Set the travel mode of a route leg for a place assignment. `direction` `"outgoing"` (default) targets the leg leaving the stop, `"incoming"` the arriving one. Pass `null` to inherit the day default. Requires `places:write`. |
| `get_assignment_participants` | Get users participating in a specific place assignment. Requires `places:read` or `places:write`. |
| `set_assignment_participants` | Set participants for a place assignment (replaces current list). Requires `places:write`. |
@@ -134,10 +142,11 @@ Requires `reservations:read` or `reservations:write` scope.
| `reorder_reservations` | Reorder reservations within a day. |
| `set_reservation_travelers` | Set who is travelling on a booking, from the trip roster (members and guests). Replaces the list; an empty array clears it. Ids that are not on the trip come back under `ignored_user_ids` rather than being attached. |
| `link_hotel_accommodation` | Set or update a hotel reservation's check-in/out day links and place. |
| `list_upcoming_reservations` | The next bookings across all of the user's trips, soonest first, for "what is coming up?" when no particular trip is in question. Hotel stays appear as their check-in and check-out moments. Cancelled bookings and archived trips are left out. `limit` defaults to 6, at most 50. Requires `reservations:read`. |
### Budget
Requires `budget:read` or `budget:write` scope. Budget addon must be enabled.
Requires `budget:read` or `budget:write` scope. The Budget addon must be enabled; it is listed as **Costs** under **Admin → Addons** (id `budget`).
| Tool | Description |
|---|---|
@@ -196,8 +205,8 @@ Requires `files:read` or `files:write`. Reading what is inside a document needs
|---|---|
| `list_trip_files` | List a trip's documents: name, type, size, uploader, description, what they are linked to, starred and trash state. Pass `trash` to list the trash instead. |
| `read_trip_file` | Read one document's contents. Text comes back as text, anything else base64, with an `encoding` field saying which. Files over 10 MB are refused; use the download link in the app. |
| `update_trip_file` | Set a file's description and the booking or place it belongs to. Pass null to detach. |
| `link_trip_file` | Link a file to one more booking, place or day assignment. |
| `update_trip_file` | Set a file's description and the booking, place or expense (`budget_item_id`, a receipt) it belongs to. Pass null to detach. |
| `link_trip_file` | Link a file to one more booking, place, day assignment or expense (`budget_item_id`, a receipt). |
| `unlink_trip_file` | Remove one link. The file stays. |
| `list_trip_file_links` | List everything a file is linked to. |
@@ -227,7 +236,7 @@ Requires `trips:share`, the same scope that manages public share links.
### Invite links
Requires `trips:share`. This is the link that grants **membership**, not the read-only public view link that `create_share_link` makes.
Requires `trips:share` together with `trips:write`; a client holding only `trips:share` does not see these three tools. This is the link that grants **membership**, not the read-only public view link that `create_share_link` makes.
| Tool | Description |
|---|---|
@@ -253,6 +262,7 @@ Requires `journey:read`, or `journey:write` to attach. Needs a configured Immich
| `search_provider_photos` | Search the connected photo library. |
| `list_provider_albums` | List its albums. |
| `list_provider_album_photos` | List one album's photos. |
| `add_journey_provider_photos` | Attach photos from the library to a journey, or to one of its entries, by the asset ids the tools above return. Requires `journey:write` and the Journey addon; listed under [MCP-Addon-Tools](MCP-Addon-Tools). |
Photo bytes are never returned: those are image URLs the app renders.
+1 -1
View File
@@ -17,7 +17,7 @@ The scopes required for Mapbox GL are:
## Satellite view
A round button in the bottom-left corner of the Leaflet map flips the base layer between the normal map tiles and **satellite** imagery (ESRI World Imagery — no API key needed, usable up to zoom 19). The icon always shows the layer it switches to. Your choice is stored on your account (`map_base_layer`), so it carries over to every trip and survives a reload. Markers, route lines, tracks and booking overlays are drawn on top of either layer.
A round button in the bottom-left corner of the map flips the base layer between the normal map tiles and **satellite** imagery (ESRI World Imagery — no API key needed, usable up to zoom 19). The icon always shows the layer it switches to. The button is on all three renderers: Leaflet swaps its tile layer for the imagery, while MapLibre GL and Mapbox GL put the same imagery on as a raster layer of their own beneath everything TREK draws, so the route, the pins and the tracks stay on top of it. Your choice is stored on your account (`map_base_layer`), the same setting whichever renderer you use, so it carries over to every trip and survives a reload. Markers, route lines, tracks and booking overlays are drawn on top of either layer.
## Place markers
+3 -2
View File
@@ -66,8 +66,9 @@ never stored inside the tile URL itself, so switching keys later does not break
Other providers are unaffected, and a keyless CARTO template is treated as "not configured" so the map falls back to
OpenFreeMap instead of drawing watermarked tiles. Save a key and your template is kept as you entered it.
Offline pre-download works on OpenFreeMap, on CARTO and on the Amap presets. It does not work on the OpenStreetMap
presets, whose tile servers do not permit bulk downloading.
Offline pre-download works on every built-in preset: OpenFreeMap, CARTO, the Amap presets, Stadia Smooth and the two
OpenStreetMap presets. The OpenStreetMap tile servers do not permit bulk downloading under their usage policy, so for a
trip you keep offline prefer OpenFreeMap, CARTO with a key or the Amap presets.
## Amap (高德地图) basemap
+1 -1
View File
@@ -34,7 +34,7 @@ Set the following environment variables before starting the server:
| Variable | Required | Description |
|---|---|---|
| `APP_URL` | Yes | Base URL of your TREK instance (e.g. `https://trek.example.com`). Used to build the redirect URI. **Env var only — not configurable via the admin panel.** If it is unset (or not a parseable URL, in which case it is ignored), TREK falls back to the first `ALLOWED_ORIGINS` entry, and only if that one is missing or unparseable to `http://localhost:{PORT}` — which produces a redirect URI your IdP will reject. A scheme-less `ALLOWED_ORIGINS=trek.example.com` does not parse, so it ends up on localhost too. |
| `APP_URL` | Yes | Base URL of your TREK instance (e.g. `https://trek.example.com`). Used to build the redirect URI. **Env var only — not configurable via the admin panel.** If it is unset, TREK falls back to the first `ALLOWED_ORIGINS` entry, and only if that one is missing or unparseable to `http://localhost:{PORT}` — which produces a redirect URI your IdP will reject. A scheme-less `ALLOWED_ORIGINS=trek.example.com` does not parse, so it ends up on localhost too. A set but unparseable `APP_URL` does not fall back: the server refuses to start with `Invalid environment configuration` naming `APP_URL`. |
| `OIDC_ISSUER` | Yes | Issuer URL of your identity provider. Must use HTTPS in production. |
| `OIDC_CLIENT_ID` | Yes | OAuth 2.0 client ID registered with your IdP. |
| `OIDC_CLIENT_SECRET` | Yes | OAuth 2.0 client secret. |
+2 -2
View File
@@ -45,7 +45,7 @@ On login, when the browser comes back online, and when you lift **Force offline
- Trips, days, places, packing items, to-dos, budget items, reservations, accommodations, trip members, tags, and categories.
- File attachments that are neither photos nor videos (PDFs, documents, etc.) are downloaded and stored as blobs in IndexedDB. Videos are deliberately skipped — a single clip can be hundreds of megabytes and would evict the trip's real documents.
- Map tiles are pre-fetched into the service-worker `map-tiles` cache for zoom levels 10–16 across each trip's bounding box, stopping at the zoom level that would push the total past 12 288 tiles (roughly 180 MB).
- Map tiles are pre-fetched into the service-worker `map-tiles` cache for zoom levels 0 to 16 across each trip's bounding box, stopping at the zoom level that would push the total past 12 288 tiles (roughly 180 MB). If the browser refused persistent storage, prefetching stops at zoom 12 so the app shell cannot be evicted.
- The places around each trip, up to 3000 from the [TREK Places API](TREK-Places-API) in one request (about a megabyte for a city), so place search and suggestions still answer offline. They are downloaded whether or not **Store map tiles offline** is on, refreshed only when the trip's area changes, and removed with the trip. See [Searching offline](Places-and-Search#searching-offline).
> **Note:** A WebSocket reconnect does *not* run this sync. It replays your queued changes and then re-reads the trip you currently have open — days, places, packing items, to-dos, budget items, reservations and files — which refreshes that one trip's cached rows. It never re-downloads the bundles for your other trips, the file blobs or the map tiles; skipping the full sync there is deliberate, so a dropped socket on an otherwise online device doesn't run into the server's rate limiter.
@@ -82,7 +82,7 @@ The stats panel shows cached trips, pending changes, conflicts and failed change
## Limitations
- Offline **editing** is supported for places and packing items (with conflict detection). Other entities — budget, to-dos, reservations, days — require connectivity to edit; while forced offline those edits still go to the live server when a connection is actually present.
- Offline **editing** is supported for places and packing items (with conflict detection), plus a visit's start and end time, its **End the day here** flag and the road trip **Driving settings** (queued, without conflict detection). Other entities — budget, to-dos, reservations, days — require connectivity to edit; while forced offline those edits still go to the live server when a connection is actually present.
- A change you made offline that **deletes** an item wins over a concurrent server edit of that same item ("delete wins"); only edit-vs-edit conflicts are surfaced for resolution.
- The conflict token has one-second resolution, so two edits to the same field within the same second can't be told apart and fall back to last-write-wins (only relevant to sub-second races; normal offline windows are unaffected).
- Creating a trip requires connectivity. Trip creation is not queued, so a new trip cannot be started while offline.
+19 -1
View File
@@ -78,7 +78,7 @@ Amap does not supply place photos here. Its images come with no licence statemen
A place remembers where it came from. One picked from Amap keeps its Amap id, one picked from Google keeps its Google id, and each keeps opening against the provider that knows it, whichever provider the admin selects later.
Amap links can be pasted into the search box too; see [Pasting a map URL](#pasting-a-map-url).
Amap links can be pasted into the search box too; see [Pasting a map URL](#pasting-a-map-url). A place in China also offers **高德地图** in its **Navigation** menu, whichever provider answers searches; see [Opening a place in a map app](#opening-a-place-in-a-map-app).
### Which provider answers
@@ -192,12 +192,30 @@ The expense belongs to the **place**, not to a day. Putting the same place on se
Deleting the place deletes its linked expense too, the same way deleting a booking does.
## Rating a place
Every trip member can rate a place from 1 to 5 stars, even when place editing is restricted to certain members. Open the place's detail panel: the rating row shows the stars, the average with the number of votes in brackets, and the avatars of who voted; rest the pointer on it to see everyone's stars. Click a star to cast your vote, and click the same star again to clear it. A place nobody has rated reads **Not rated yet**.
The average also sits beside the place's name in the places list and on a marker's hover card on the map; a marker that carries no order badge shows it as a small disc in its corner instead. To narrow the list, the star button in the sidebar header (**Filter by rating**) picks a floor from 1 to 5 stars and keeps only the places whose average reaches it.
Saved places in [Collections](Collections) are rated the same way. Saving a trip place to a list or copying a list place into a trip carries the votes along, but only those of people who are members on both sides.
> **AI / MCP:** `rate_place` sets or clears your own vote; see [MCP-Tools-and-Resources](MCP-Tools-and-Resources).
## Custom place image
By default a place's thumbnail is fetched automatically (from Google or Wikimedia when the place was imported or matched, otherwise a category icon). To use your own photo instead, open the place's detail panel and click its round thumbnail. Pick an image and it becomes that place's thumbnail everywhere (list, map marker, itinerary, PDF export and shared trips). A small remove button on the thumbnail clears the custom image and restores the automatic default. Accepted formats are JPG, PNG, GIF and WebP (HEIC is converted automatically), up to 20 MB.
The same control is available on saved places in [Collections](Collections#place-detail).
## Opening a place in a map app
The **Navigation** button in a place's detail panel, on the phone's place sheet and on a saved place in Collections opens a short menu of map apps, in this order: **Google Maps**, **Waze**, **Apple Maps**, **OpenStreetMap**, **CoMaps** and, for a place in China, **高德地图** (Amap). Waze starts navigating straight away; the others open the place, and starting navigation from there is one tap. When only one app is available the button opens it directly.
Which entries appear depends on the place and on where you are, not on the search provider the admin picked. Apple Maps is left out on Android. Amap is offered by where the place is, because it only has a map of China: a stop in Shanghai gets it whoever is planning the trip, a stop in Lisbon never does. Waze, Apple Maps, CoMaps and Amap need the place's coordinates; Google Maps and OpenStreetMap can still open a place that has none, Google from its name and address, OpenStreetMap from its name. Coordinates handed to Amap are converted to its own datum on the way out, so the pin lands on the right street.
In the installed app the map app takes over the current window rather than a new tab, so coming back lands you where you were.
## Importing multiple places
Drag a `.gpx`, `.kml`, or `.kmz` file onto the Places sidebar to import all waypoints or features at once. You can also import a shared list with the **List Import** button in the sidebar header (it reads **Google List** when Google is the only list source): both Google Maps and Naver Maps list URLs are supported, and a shared Google Maps route works too, its stops becoming places in driving order. Stops a route gives only by name are looked up through the TREK API first.
+10
View File
@@ -137,6 +137,16 @@ whatever happens, so a provider that misses that window is left out of the list
than holding it up. `near` is the coordinate the search is biased toward,
and it is what tells the four places called "Hase-dera" apart — use it when you have it.
The Road trip search along the route reaches the same hook. It calls `search` once per
kind the person picked, with two more fields on the request: `category` (`fuel`,
`charging`, `rest_area`, `campsite`, `restaurant`, `sights` or `hotel`) and `bounds`
(`{ south, west, north, east }`, the rectangle to search, with `near` at its centre).
`query` is then a fixed English phrase such as `EV charging station` rather than
something typed, so an index that ignores `category` still gets a usable query. Hits
outside `bounds` are dropped, and so is a hit whose own `category` names a different one
of those seven kinds. Both fields are absent on ordinary searches, so read them as
optional.
## Raise validation warnings on a trip
**Needs:** `hook:trip-warning-provider`
+8 -6
View File
@@ -571,7 +571,9 @@ The frame's CSP is locked down per plugin: `default-src 'none'`, own inline
scripts/styles + the plugin's **own** `/plugin-frame/<id>/` files only (no other
host may serve it script/style/img), `connect-src` limited to the hosts you were
**granted** via `http:outbound:<host>` permissions (not merely the `egress[]` you
declared), no popups.
declared), no popups. The frame is allowed to go **fullscreen** (`allow="fullscreen"`
on the iframe), so a page or widget may call `requestFullscreen()` on its own content
after a click or tap; popups stay blocked.
### The context payload
@@ -806,7 +808,7 @@ module.exports = definePlugin({
| Hook | Permission | Status |
|---|---|---|
| `placeDetailProvider.getDetails(placeId, ctx)` → `{ label, value?, url? }[]` | `hook:place-detail-provider` | **live** — shown in the place-detail panel; also `GET /api/place-details/:placeId` |
| `searchProvider.search(request, ctx)` → `SearchResultPlace[]` | `hook:search-provider` | **live** — answers place searches from an index TREK does not ship, drawn into the app's own search list beside the core results (#2221). `request` is `{query, limit, lang?, near?}`; each place is `{id?, name, lat, lng, address?, rating?, website?, phone?, category?, description?}`. `rating` is the field open data cannot answer — OpenStreetMap carries none — so it is what makes "the best rated one around here" answerable at all; it is clamped to 0..5. Coordinates are range-checked, strings capped, `website` must be http/https, and ids are namespaced to `plugin:<yourId>:<id>` so they can never collide with an OSM one. Called for an explicit search, **not** per keystroke. Also `GET /api/plugin-search`, and to a connected assistant as the `search_places_via_plugins` MCP tool |
| `searchProvider.search(request, ctx)` → `SearchResultPlace[]` | `hook:search-provider` | **live** — answers place searches from an index TREK does not ship, drawn into the app's own search list beside the core results (#2221). `request` is `{query, limit, lang?, near?, category?, bounds?}`; each place is `{id?, name, lat, lng, address?, rating?, website?, phone?, category?, description?}`. `rating` is the field open data cannot answer — OpenStreetMap carries none — so it is what makes "the best rated one around here" answerable at all; it is clamped to 0..5. Coordinates are range-checked, strings capped, `website` must be http/https, and ids are namespaced to `plugin:<yourId>:<id>` so they can never collide with an OSM one. Called for an explicit search, **not** per keystroke. The Road trip search along the route calls it too, once per kind the person picked, with `category` (`fuel`, `charging`, `rest_area`, `campsite`, `restaurant`, `sights` or `hotel`) and `bounds` (`{south, west, north, east}`, with `near` at its centre); `query` is then a fixed English phrase for that kind such as `fuel station` or `EV charging station`. The host drops every hit outside `bounds`, and a hit whose own `category` names a different one of those seven kinds; the rest count as the requested kind. Both fields are absent on ordinary searches. Also `GET /api/plugin-search`, and to a connected assistant as the `search_places_via_plugins` MCP tool; the corridor search is `POST /api/roadtrip/search-area` and the `search_roadtrip_corridor` MCP tool |
| `warningProvider.getWarnings(tripId, ctx)` → `{ level, message, dayId?, placeId? }[]` | `hook:trip-warning-provider` | **live** — validation warnings shown as a non-blocking banner in the trip planner; also `GET /api/trip-warnings/:tripId`, and to a connected assistant as the `get_trip_warnings` MCP tool (≤20 warnings per provider, message ≤300 chars) — that path needs only the trips read scope, not `plugins:use` |
| `tableContributor.getContributions(view, tripId, ctx)` → `TableContribution[]` | `hook:table-contributor` | **live** — host-rendered **columns/actions** keyed by `entityId` in the reservations, transports, places, day, costs, packing, files and todos views. A `column` is `{kind:'column', entityId, id, label, value?, url?, icon?, tone?}` (url is http/https/mailto only); an `action` is `{kind:'action', entityId, id, label, icon?, target}` where `target` opens your sandboxed frame (`{kind:'frame', sub}`) or calls a route (`{kind:'route', method, sub}`). All fields are bounded + normalized host-side; also `GET /api/view-contributions/:view/:tripId` |
| `mapMarkerProvider.getMarkers(tripId, ctx)` → `MapMarkerContribution[]` | `hook:map-marker-provider` | **live** — bounded markers overlaid on the trip map (#587). Each is `{id, lat, lng, label?, popupText?, url?, icon?, tone?}`; coordinates are range-checked (−90..90 / −180..180), text length-capped, url http/https/mailto-only, count capped (≤200/plugin). Declarative only — plugin JS never runs on the map canvas. Also `GET /api/map-markers/:tripId` |
@@ -1142,9 +1144,9 @@ id (never a parent or a user id).
Delivery is fire-and-forget on a short timeout, so a slow subscriber never blocks a
core write. Because there's no user, trip reads (`ctx.trips.*`) are refused inside a
handler — beyond the snapshot, use the plugin's own `ctx.db`, `ctx.ws.*`, or an
outbound call. A plugin's own `plugin:*` broadcasts are never delivered back, so
handlers can't loop. Event names follow `<family>:<verb>` (`place:created`,
handler, and so are `ctx.ws.*` broadcasts; beyond the snapshot, use the plugin's own
`ctx.db` or an outbound call. A plugin's own `plugin:*` broadcasts are never
delivered back, so handlers can't loop. Event names follow `<family>:<verb>` (`place:created`,
`day:updated`, `file:*`, `assignment:*`, `budget:*`, `accommodation:*`, …); the SDK
exports the authoritative family list and the snapshot-grant mapping as
`EVENT_FAMILIES` and `EVENT_SNAPSHOT_GRANT`.
@@ -1451,7 +1453,7 @@ build arg defaults to the literal `dev` — has nothing to compare a range again
the check is skipped and an unversioned build installs anything. Plugins should still
guard optional `ctx.*` namespaces.
**Permissions** — the commonly-used core subset below; the **full list of 64**
**Permissions** — the commonly-used core subset below; the **full list of 65**
(all read/write scopes, the notify/ai/oauth brokers, every provider hook, `mcp:tools`)
lives in **[[Plugin Permissions|Plugin-Permissions]]**. Unknown values are rejected at
install (and by `trek-plugin validate` and registry CI, which check against the same list).
+1 -1
View File
@@ -58,7 +58,7 @@ ungranted capability is physically unreachable**, not just disallowed. See
| `hook:photo-provider` | Register as a photo provider in Memories | Implement the `PhotoProvider` interface. |
| `hook:calendar-source` | Register as a calendar source | Implement the `CalendarSource` interface. |
| `hook:place-detail-provider` | Contribute extra details (reviews, ratings, links) to a place via the `hooks.placeDetailProvider` provider hook | Implement `PlaceDetailProvider` in `hooks` on the plugin definition (not on `ctx`) — shown in the place-detail panel; also exposed at `GET /api/place-details/:placeId`. |
| `hook:search-provider` | Answer place searches from an index TREK does not ship, beside its own results | Implement `SearchProvider` in `hooks` on the plugin definition — hits are drawn into the app’s search list; also exposed at `GET /api/plugin-search` and as the `search_places_via_plugins` MCP tool. Only these results can carry a `rating`: open data has none. |
| `hook:search-provider` | Answer place searches from an index TREK does not ship, beside its own results | Implement `SearchProvider` in `hooks` on the plugin definition — hits are drawn into the app’s search list; also exposed at `GET /api/plugin-search` and as the `search_places_via_plugins` MCP tool. Only these results can carry a `rating`: open data has none. The Road trip search along the route calls the same hook once per kind, with `category` and `bounds` added to the request and a fixed category phrase as `query`; hits outside `bounds`, or of a different road trip kind, are dropped. |
| `hook:trip-warning-provider` | Raise validation warnings on a trip via the `hooks.warningProvider` provider hook | Implement `WarningProvider` in `hooks` on the plugin definition (not on `ctx`) — shown as a non-blocking banner in the planner; also exposed at `GET /api/trip-warnings/:tripId`, and to a connected assistant as the `get_trip_warnings` MCP tool (≤20 warnings per provider, message ≤300 chars) — that path needs only the trips read scope, not `plugins:use`. |
| `hook:map-marker-provider` | Overlay bounded **markers** on the trip map via the `hooks.mapMarkerProvider` provider hook | Implement `MapMarkerProvider` in `hooks` — returns `{id, lat, lng, label?, popupText?, url?, icon?, tone?}[]` (#587 "show bookings on map"). **Declarative only** — plugin JS never runs on the map canvas. The host range-checks coordinates (−90..90 / −180..180), String-coerces + length-caps text, allowlists the popup url (http/https/mailto), and caps the marker count (≤200) per plugin; a failing provider is skipped. Exposed at `GET /api/map-markers/:tripId`. |
| `hook:map-layer-provider` | Overlay bounded **vector layers** (polylines, polygons, metric circles) on the trip map via the `hooks.mapLayerProvider` provider hook | Implement `MapLayerProvider` in `hooks` — returns `{id, name?, features}[]` where a feature is `{type, points?/center?+radiusM?, tone?, width?, dash?, opacity?, fill?, label?}`. **Declarative only** — the host draws everything; styling is the tone palette plus clamped numerics (width 1–8, opacity 0.05–1, radius ≤2000 km) and a dash enum, so a layer can't impersonate core UI. Budgets per plugin: ≤4 layers / ≤150 features / ≤8000 vertices / ≤2000 vertices per shape; invalid or oversized shapes are dropped whole (never truncated); a failing provider is skipped. Drawn beneath TREK's own routes. Exposed at `GET /api/map-layers/:tripId`. |
+1 -1
View File
@@ -104,7 +104,7 @@ Between them they now catch, **offline**, nearly everything the registry rejects
the network allow-list and the iframe CSP from those permissions only and never reads
`egress[]`, so such a host is silently unreachable at runtime
- a permission TREK doesn't know — the SDK and registry CI check against the same
64-entry list the host enforces, so a typo'd permission fails here instead of at
65-entry list the host enforces, so a typo'd permission fails here instead of at
install on every instance (the registry's copy is a manually regenerated snapshot,
so a brand-new TREK permission can briefly fail registry CI even though `validate`
accepts it — that's the snapshot lagging, not a typo)
+3 -1
View File
@@ -52,13 +52,15 @@ A key can be narrowed when you create it, to any subset of:
`trips` · `days` · `places` · `notes` · `reservations` · `accommodations` · `travellers` · `bucket-list` · `stats`
This is **not** the same as `include`. `include` chooses which sections a response carries and any key may ask for any of them; this decides what the key may see at all, and the server refuses the rest with a `403`. A key given only `days` and `notes` reads exactly that, however it asks.
This is **not** the same as `include`. `include` chooses which sections a response carries and any key may ask for any of them; this decides what the key may see at all, and the server refuses the rest with a `403`. `trips` is the entry ticket for both trip endpoints: without it, every call to `/api/v1/trips` and `/api/v1/trips/{id}` is a `403` with `required_scope: "trips"`, whatever else the key was given. The other areas only trim what a trip brings along. A key given `trips`, `days` and `notes` reads exactly that, however it asks.
Two rules make it predictable:
- **A section you named but may not read is a `403`**, with the missing one in `required_scope`. Silently dropping what you explicitly asked for would send you debugging your own correct code.
- **A section you did not name is simply left out.** `include` defaults to everything, so a narrow key that omits `include` gets what it may have rather than an error for wanting sections it never mentioned.
A key without `days` still receives the day skeleton when it asks for `places`, `notes` or `reservations`, because those are reported on days; each day then carries its `date` and `day_number`, and its `title` and `notes` are `null`.
Keys created before this existed, and keys created with everything selected, read all of it — nothing that worked before stops working.
## Endpoints
+1 -1
View File
@@ -50,7 +50,7 @@ A public viewer has no account, so there is no "their" display currency to use.
The shared trip page renders a branded read-only interface with a dark hero header showing the trip title, description, and date range. A tab bar at the top provides access to the sections you enabled. The viewer can switch the display language using a language picker in the top-right corner.
The Plan tab appears whenever **Map & Plan** is on — which is every link created through the share UI, since that toggle is locked on there. A link whose `share_map` flag was turned off outside the UI (through the REST API or the `create_share_link` MCP tool, both of which take it as a plain boolean) has no Plan tab at all: the server withholds the days, places, assignments and notes entirely, and the viewer opens on the first section the owner did share. A **day picker** sits directly above the map — an **All** pill plus one pill per day (**Day 1**, **Day 2**, …) — and drives the same selection as the day cards below it, so the map and the expanded day never disagree. Pick a day and its stops are numbered on the map in visiting order (a place the day returns to shows both positions on one marker, e.g. `1 · 3`) and joined by a dashed connector. That connector is a straight line showing sequence, not a driving route: TREK will not send a shared itinerary to a third-party routing service on an anonymous visitor's behalf. On **All** the map shows every geocoded place as an unnumbered pin with no connector. Below the map sits a collapsible day-by-day itinerary (with places, notes, and transport inline when Bookings is enabled) and accommodation badges per day.
The Plan tab appears whenever **Map & Plan** is on — which is every link created through the share UI, since that toggle is locked on there. A link whose `share_map` flag was turned off outside the UI (through the REST API or the `create_share_link` MCP tool, both of which take it as a plain boolean) has no Plan tab at all: the server withholds the days, places, assignments and notes entirely, and the viewer opens on the first section the owner did share. A **day picker** sits directly above the map — an **All** pill plus one pill per day (**Day 1**, **Day 2**, …) — and drives the same selection as the day cards below it, so the map and the expanded day never disagree. Pick a day and its stops are numbered on the map in visiting order (a place the day returns to shows both positions on one marker, e.g. `1 · 3`) and joined by a dashed connector. That connector is a straight line showing sequence, not a driving route: TREK will not send a shared itinerary to a third-party routing service on an anonymous visitor's behalf. On **All** the map shows every geocoded place as an unnumbered pin with no connector. On either view, pins that sit close together are grouped into a cluster bubble with their count, the same way the planner's map does it, so the stops of a busy day stay tappable on a phone; click a cluster to zoom in on its members, and at maximum zoom it fans them out. Below the map sits a collapsible day-by-day itinerary (with places, notes, and transport inline when Bookings is enabled) and accommodation badges per day.
The Chat tab (when enabled via `share_collab`) shows chat messages grouped by date with sender avatars. Viewers cannot send messages.
+11 -6
View File
@@ -1,6 +1,6 @@
# Real-Time Collaboration
TREK keeps every trip in sync across all connected members without requiring a page refresh. A dedicated **Collab addon** adds a second layer on top of that sync: group chat, shared notes, polls, and a "What's Next" widget showing upcoming assigned places.
TREK keeps every trip in sync across all connected members without requiring a page refresh. A dedicated **Collab addon** adds a second layer on top of that sync: group chat, shared notes, shared links, polls, and a "What's Next" widget showing upcoming assigned places.
![The Collab tab: chat, shared notes, polls and the What's Next widget side by side](assets/Collab.png)
@@ -28,18 +28,23 @@ An origin rejection happens before the socket exists, so it produces no close co
## The Collab addon
The Collab addon (`collab`) must be enabled by an admin before the panel is visible inside a trip. Once enabled, it provides four sub-features that can each be toggled independently:
The Collab addon (`collab`) must be enabled by an admin before the panel is visible inside a trip. Once enabled, it provides five sub-features that can each be toggled independently:
| Sub-feature | What it provides |
|-------------|-----------------|
| **Chat** | Group chat with reactions, replies, and URL previews |
| **Chat** | Group chat with reactions, replies, images, and URL previews |
| **Notes** | Categorized, pinnable, markdown-formatted shared notes |
| **Links** | Shared web addresses with a title, pinnable, synced live between members |
| **Polls** | Single- or multiple-choice votes with optional deadlines |
| **What's Next** | Upcoming assigned places across all trip days |
> **Admin:** enable the Collab addon and individual sub-features in [Admin-Addons](Admin-Addons).
> **Admin:** enable the Collab addon and individual sub-features in [Admin-Addons](Admin-Addons). Links is on by default, like the other four.
On **desktop** the panel shows Chat as a fixed 380 px column on the left when other sub-features are also enabled; if only Chat is on, it expands to fill the full width. Notes, Polls, and What's Next share the remaining space on the right. On **mobile** a tab bar at the top lets you switch between the enabled sub-features one at a time. Disabled sub-features are hidden from the tab bar.
On **desktop** the panel shows Chat as a fixed 380 px column on the left when other sub-features are also enabled; if only Chat is on, it expands to fill the full width. Notes, Links, Polls, and What's Next share the remaining space on the right. On **mobile** a tab bar at the top lets you switch between the enabled sub-features one at a time. Disabled sub-features are hidden from the tab bar.
### Links
The **Links** tab collects the web addresses a trip runs on: the booking portal, the shared photo album, the restaurant's menu. **Add link** asks for a title and an `http(s)` address; anything else is refused. Each link opens in a new tab, and members with `collab_edit` can **pin** it to the top of the list or **delete** it. Pinned links come first, then the newest. Adding, pinning and deleting show up for every connected member at once.
## Conflict handling
@@ -51,7 +56,7 @@ There is no operational-transform or CRDT merge — simultaneous edits to the sa
## Access control
All Collab reads require trip membership. Writing — sending messages, creating notes, creating polls, voting — requires the `collab_edit` permission. Members without `collab_edit` can read but cannot post or interact.
All Collab reads require trip membership. Writing — sending messages, creating notes, adding links, creating polls, voting — requires the `collab_edit` permission. Members without `collab_edit` can read but cannot post or interact.
## Related pages
+2 -2
View File
@@ -51,7 +51,7 @@ Each card displays:
- **Status dot** — green for Confirmed, amber for Pending
- **Type chip** — icon and label for the reservation type
- **Needs review badge** — an amber badge shown on reservations flagged by importers that may need your attention
- **Review badge** — an amber badge shown on reservations flagged by importers that may need your attention
- **Title** — the reservation name
- **Edit and delete buttons** — visible only if you have edit permission
@@ -133,7 +133,7 @@ The **Import from file** button is hidden only when neither the `kitinerary-extr
### Needs review flag
Items that the extractor could only partially parse are flagged **Needs review** — an amber badge on the card. Review these reservations after import and fill in any missing fields manually.
Items that the extractor could only partially parse are flagged for review — an amber **Review** badge on the card. Review these reservations after import and fill in any missing fields manually.
### AI fallback for hard-to-read files
+8
View File
@@ -18,6 +18,14 @@ When the trip has a start and an end date and you may edit the day, that menu al
Route segments reset at any transport reservation (flight, train, car, bus, or cruise) between two places — that leg is not driven or walked, so no ground route is drawn across it.
### Counting routing requests
> **Admin:** counting is on by default. To switch it off, set `route_usage_enabled` to `false` in the `app_settings` table of the database; there is no screen for it.
Every route is worked out in the browser against the routing host, so the server never sees a request of its own. The browser therefore tallies what it asks for and posts the totals in batches, and the instance keeps one row per day, routing profile and kind of request: how many requests, how many waypoints, roughly how many kilometres, how many came back without a route, and whether they went to the public hosts or to your own engine. Counters only: no coordinate, no route, no user and no trip are in them, and they never leave the instance.
Signed in as an admin, `GET /api/route-usage/summary` returns the totals, the requests per day, the busiest day, the split per profile and per kind of request, and the share answered by a self-hosted engine. `DELETE /api/route-usage` wipes the counters. Rows older than 400 days are deleted every night, whether counting is on or off.
## Route display
- Colored line segments connect consecutive places on the map.
+2 -2
View File
@@ -14,7 +14,7 @@ A production TREK deployment checklist. All items reference actual TREK configur
- [ ] Set `TRUST_PROXY=1` so client IPs are captured correctly in the audit log. In `NODE_ENV=production` this defaults to `1` automatically, but set it explicitly if you use a non-standard proxy hop count.
- [ ] Set `FORCE_HTTPS=true` to 301-redirect HTTP to HTTPS and add `upgrade-insecure-requests` to the CSP. Your proxy must send `X-Forwarded-Proto: https` (or terminate TLS on the same connection), otherwise the redirect fires on every request and loops.
- [ ] Know that HSTS (`max-age=31536000`) does not depend on `FORCE_HTTPS`: it is sent whenever `FORCE_HTTPS=true` **or** `NODE_ENV=production`, which the Docker image sets by default — so an instance behind Traefik, Caddy or a Cloudflare Tunnel advertises HSTS without setting `FORCE_HTTPS` at all. Set `HSTS_INCLUDE_SUBDOMAINS=true` to add `includeSubDomains`; it is off by default so an install on an apex domain does not force HTTPS onto sibling subdomains you may still serve over plain HTTP.
- [ ] Keep `ALLOW_INTERNAL_NETWORK=false` unless a strict-guard integration — Immich, Synology Photos, AirTrail, a notification webhook or ntfy — is on your LAN. See [Internal-Network-Access](Internal-Network-Access). Note: loopback (`127.x`, `::1`) and link-local (`169.254.x`) addresses are always blocked regardless of this setting.
- [ ] Keep `ALLOW_INTERNAL_NETWORK=false` unless a strict-guard integration — Immich, Synology Photos, AirTrail, Dawarich, a document store bound through [Document-Sync](Document-Sync) (Paperless-ngx, Papra, Nextcloud, OpenCloud, Synology Drive), a notification webhook or ntfy — is on your LAN. See [Internal-Network-Access](Internal-Network-Access). Note: loopback (`127.x`, `::1`) addresses are always blocked regardless of this setting, and so are link-local (`169.254.x`) addresses, apart from a single one you name in `ALLOW_LINK_LOCAL_IPS`, which the strict guard then treats as internal. See [A link-local address you need](Internal-Network-Access#a-link-local-address-you-need).
## Authentication
@@ -71,7 +71,7 @@ Installed plugins run **untrusted third-party code**. TREK contains a plugin in
- [ ] Leave the plugin system's defaults in place. It is **on by default** but installed plugins still have to be **activated one by one**, so no third-party code runs until an admin turns a specific plugin on. Set `TREK_PLUGINS_ENABLED=false` (accepts `false`/`0`/`off`/`no`) to switch the whole system off — installed plugins stay on disk, deactivated, and the runtime is idle.
- [ ] Keep the **OS permission jail** enabled (the default). In production each plugin runs in an isolated child process launched with Node's `--permission` model: filesystem **writes**, `child_process`, worker threads and native addons are denied outright, and reads are scoped to just the plugin's own code — so a plugin cannot read `trek.db` or the secret files, or shell out. The child's environment is scrubbed (no `JWT_SECRET`, no DB credentials). Setting `TREK_PLUGIN_PERMISSIONS=off` disables this jail (isolation then falls back to crash-only) and logs a loud warning — only ever do this on a machine you fully trust.
- [ ] Rely on the **private-egress block** (SSRF backstop). Even a plugin that declared an outbound host cannot reach a destination that resolves to a loopback, private, link-local, ULA, carrier-grade-NAT, cloud-metadata (`169.254.169.254`), multicast or reserved address — the guard re-checks the resolved IP, so a plugin can't pivot to internal services or DNS-rebind to them. This is independent of `ALLOW_INTERNAL_NETWORK` (which governs the core integrations reached through the strict guard — Immich, Synology Photos, AirTrail, notification webhooks and ntfy — not plugin egress). The one escape hatch is `TREK_PLUGIN_ALLOW_PRIVATE_EGRESS=on`, for a plugin that has to reach a service on your LAN (a Gotify, an ntfy, an Ollama). It is instance-wide rather than per plugin, and it lifts the block entirely rather than narrowing it: a declared host may then resolve to any address, the `169.254.169.254` metadata IP included, and unix-socket or named-pipe connects (`docker.sock`, a local database socket) are permitted without even being declared, since the OS jail does not gate socket connects. Leave it unset unless a specific plugin genuinely needs a LAN target.
- [ ] Rely on the **private-egress block** (SSRF backstop). Even a plugin that declared an outbound host cannot reach a destination that resolves to a loopback, private, link-local, ULA, carrier-grade-NAT, cloud-metadata (`169.254.169.254`), multicast or reserved address — the guard re-checks the resolved IP, so a plugin can't pivot to internal services or DNS-rebind to them. This is independent of `ALLOW_INTERNAL_NETWORK` (which governs the core integrations reached through the strict guard — Immich, Synology Photos, AirTrail, Dawarich, the Document-Sync stores, notification webhooks and ntfy — not plugin egress). The one escape hatch is `TREK_PLUGIN_ALLOW_PRIVATE_EGRESS=on`, for a plugin that has to reach a service on your LAN (a Gotify, an ntfy, an Ollama). It is instance-wide rather than per plugin, and it lifts the block entirely rather than narrowing it: a declared host may then resolve to any address, the `169.254.169.254` metadata IP included, and unix-socket or named-pipe connects (`docker.sock`, a local database socket) are permitted without even being declared, since the OS jail does not gate socket connects. Leave it unset unless a specific plugin genuinely needs a LAN target.
- [ ] The supervisor caps each plugin's **resident memory** (default 300 MB, `TREK_PLUGIN_MAX_RSS_MB`) — measured host-side from the OS, never the plugin's self-report — and kills a plugin that blows the ceiling or stops sending heartbeats; repeat offenders auto-disable. Every `ctx.*` capability call is also **rate-limited** at the dispatch boundary (a token bucket: ~60-call burst, 20 calls/sec sustained, 16 concurrent; `TREK_PLUGIN_RPC_BURST` / `TREK_PLUGIN_RPC_PER_SEC` / `TREK_PLUGIN_RPC_INFLIGHT`), so one plugin in a tight loop gets throttled instead of freezing the instance.
- [ ] Review the **capability audit** if you grant plugins broad data access. Every host-mediated core-data read and broadcast a plugin makes is recorded at the RPC boundary against the real acting user (not a value the plugin supplies) in a per-plugin, hash-chained, tamper-evident log. Admins see it per plugin; each user can see "what have plugins done in my name?". Retention is capped per plugin (default 20 000 rows, `TREK_PLUGIN_AUDIT_MAX_ROWS`).
+5 -1
View File
@@ -10,7 +10,7 @@ Open the **Transports** tab in the trip planner and click **Add**, or open the p
## Public transit search
The **Add transport** dialog has two modes: **Manual** (the classic form) and **Automated** — a public-transit route search powered by [Transitous](https://transitous.org/), free open data with no API key or paid provider. The **transit button** (tram icon) on each day header opens the dialog straight in the Automated mode. (The rename pencil this button replaced moved next to the day name in the day detail panel.)
The **Add transport** dialog has two modes: **Manual** (the classic form) and **Automated** — a public-transit route search powered by [Transitous](https://transitous.org/) by default, free open data with no API key or paid provider; an admin can switch the backend to Google, see below. The **transit button** (tram icon) on each day header opens the dialog straight in the Automated mode. (The rename pencil this button replaced moved next to the day name in the day detail panel.)
You can also start the search from a single leg: click the travel-time connector between two stops in the day plan and pick **Public transit** from the menu. The search opens with that leg's start and end already filled in and the departure time taken from the stop you are leaving.
@@ -23,6 +23,8 @@ The mode switch only appears when the trip has a **start date and an end date**
Self-hosters can point the `TRANSIT_API_URL` environment variable at their own MOTIS instance.
> **Admin:** which service answers the search is set under **Admin → Settings → API Keys → Transit Provider**. **Transitous (free)** is the default: community GTFS feeds, keyless, with the best coverage in Europe. **Google** runs the stop search and the route plan through the Google Maps API key in the same card, for regions Transitous has no data for. Google bills per search, and the key has to be allowed to call the **Routes API** and the **Places API (New)** (text search); the legacy Directions API is not used. A key saved only in a member's own settings serves that member alone, so save it as an admin to apply it instance-wide. While no Google key resolves, the search quietly stays on Transitous even with Google selected, which is why an empty result names the backend that answered (*No connections found via Google*).
## Transport types
Nine types are available: **Flight**, **Train**, **Bus**, **Car**, **Taxi**, **Bicycle**, **Cruise**, **Ferry**, and **Other**.
@@ -60,6 +62,8 @@ Departure and arrival fields use the **generic location picker** — type at lea
For the **Car** type the date fields are relabelled to match a rental: the departure side reads **Pickup** and **Pickup time**, the arrival side **Return** and **Return time**. There is no separate car-rental type — a rental and your own car are both logged as Car.
A car booking can also carry **Stops along the way**, the places the drive passes through between pickup and return. The block sits between the **To** picker and the pickup date: **Add stop** adds a row with the location picker and an optional time, a stop whose location was never picked is dropped on save, and the arrows on the left move a stop up or down. That order is the route: on the map the drive is routed through the stops in that order, so the line runs via each stop instead of straight from pickup to return. The same block is on the phone's transport form.
## Flight-specific fields
When the type is set to Flight, three additional fields appear on every leg:
+1
View File
@@ -59,6 +59,7 @@ Once added, a guest can be picked anywhere a member can:
- **Packing** — assigned to packing items and categories (see [Packing-Lists](Packing-Lists)).
- **To-dos** — set as a task assignee.
- **Day plan** — added as a participant on activities and places.
- **Bookings**: set as a traveler on a reservation or a transport (see [Reservations-and-Bookings](Reservations-and-Bookings)).
### What a guest can never do
+4 -2
View File
@@ -27,6 +27,8 @@ Each sidebar has a drag handle on its inner edge for resizing.
A **Day Detail panel** floats over the map area when you open a specific day, showing the weather forecast, that day's reservations, and the accommodation block. It can be collapsed to a slim header bar without closing it.
Opening a day also narrows the Places sidebar. With a day selected, the **Planned** filter lists and counts only the places on that day's plan, the same set the map draws, and a line under the filter tabs says **Showing the open day only**. Its **X** closes the day again, so list, count and map return to the whole trip. **All** and **Unplanned** stay trip-wide on purpose: a place on some other day is planned, whichever day happens to be open.
## Tabs
The tab bar sits directly below the main navigation bar.
@@ -130,9 +132,9 @@ GDACS warning popups show the current episode score when supplied, falling back
### Accommodation portals in Roadtrip
The Add as a stop dialog offers trivago and CHECK24 for hotels, or PiNCAMP and Pitchup for campsites. The portal opens in a new tab without adding a stop or making a booking. trivago receives the search text; PiNCAMP receives a geographic area extending roughly 20 km from the stop; Pitchup receives the stop coordinates. All three receive the trip dates when both days have dates and departure is after arrival. Review the destination, dates and guest count on the portal. CHECK24 requires a manual search. Check-in and check-out use the shared TREK time picker.
The Add as a stop dialog offers trivago and CHECK24 for hotels, or PiNCAMP and Pitchup for campsites. The portal opens in a new tab without adding a stop or making a booking. trivago receives the search text; PiNCAMP receives a geographic area extending roughly 20 km from the stop; Pitchup receives the stop coordinates. All three receive the trip dates when both days have dates and departure is after arrival. Review the destination, dates and guest count on the portal. CHECK24 requires a manual search. The dialog asks only for the **Check-in** time, in the shared TREK time picker; the check-out is a booking detail and is entered under Days.
All Looking for categories, including accommodation, are added as Roadtrip service stops. In Roadtrip mode, Edit in a planned place's details opens the stop dialog with its saved duration and overnight dates and times. Saving updates the existing stop; More details still opens the full place editor. The shared stop-type contract also accepts hotel through MCP.
All Looking for categories, including accommodation, are added as Roadtrip service stops. In Roadtrip mode, Edit in a planned place's details opens the stop dialog with its saved duration and check-in time. Saving updates the existing stop; More details still opens the full place editor. The shared stop-type contract also accepts hotel through MCP.
A booked night uses its check-in as the earliest arrival and the place's STAY as its length. The drive never reads the check-out: it is the latest the room has to be handed back, not the time anybody drives on, so it stays a booking detail under Days. To leave at a set hour, give the visit an End, see [Road-Trip](Road-Trip#leaving-at-a-set-time). Explicit arrival times remain authoritative. Browser planning and MCP use the same scheduling logic.
+2 -2
View File
@@ -115,9 +115,9 @@ There is no button for it: the Admin Panel UI has no per-user MFA reset (the use
---
## Demo user cannot edit or create
## Demo user cannot upload files or change account settings
**Cause:** The instance is running with `DEMO_MODE=true`. All write operations are blocked for the demo account by design.
**Cause:** The instance is running with `DEMO_MODE=true`. For the demo account, file uploads (avatar, trip cover, documents, place and collection images), password change, account deletion, MFA changes and the MCP write tools answer 403 by design. Everything else the demo user can create, edit and delete, trips, days, places and costs included; the hourly reset puts it all back to the saved baseline.
**Fix:** This is intentional behavior for public demo deployments. If you are self-hosting and want full access, remove the `DEMO_MODE` variable (or set it to `false`). See [Demo Mode](Demo-Mode).
+1 -1
View File
@@ -42,7 +42,7 @@ A user-verified **passkey** satisfies this policy the way a TOTP authenticator d
Whether enrolling a passkey is a way *out* of the lockout depends on the instance. Passkey login is off by default (see [Passkeys](Passkeys)); where an admin has turned it on, the enrolment endpoints stay reachable while the rest of the API is blocked, so you can add a passkey from **Settings → Account** instead of setting up an authenticator app. Where it is off, TOTP setup is the only way to unblock yourself — or ask an admin to reset your 2FA.
> **Admin:** You can reset 2FA for a locked-out user from the admin panel. See [Admin-Users-and-Invites](Admin-Users-and-Invites).
> **Admin:** You can clear a locked-out user's 2FA with `DELETE /api/admin/users/<id>/mfa` (admin session required; it refuses your own account). The admin panel has no button for it: the user modal offers only **Reset passkeys**. See [Troubleshooting](Troubleshooting#locked-out-of-mfa--lost-authenticator).
## Rate limits
+17
View File
@@ -80,6 +80,23 @@ See [Install-Helm](Install-Helm) for the full installation walkthrough and value
TREK runs any pending database migrations automatically at startup. No manual migration steps are required after pulling a new image.
## After the Update
The first time a user opens TREK on the desktop after an update, a notice headed **Update installed** opens on top of the app: three cards with the headline features of that release, a note from the maintainer, a **Release notes** link to the full notes on GitHub, and links for supporting the project. Every user sees it once per version. Closing it keeps it closed on that version, and the next update brings it back, a patch release included. The phone does not show it.
The full notes for every version are also in the admin panel under the **GitHub** tab. See [Admin-GitHub-Releases](Admin-GitHub-Releases).
## Upgrading to 4.3.0
A few defaults change with 4.3.0. None of them needs a manual step, but they are worth knowing before you pull the image:
- **Place search asks a new service.** Suggestions, the full search, the category buttons, import geocoding, offline downloads and the Road trip search now ask the TREK Places API at `https://places.liketrek.com`, so your server makes outbound requests to that host. Set `TREK_PLACES_ENABLED=false` to keep searching the way it did before, or `TREK_PLACES_URL` to point at a copy you run yourself. [TREK-Places-API](TREK-Places-API) lists what is sent.
- **New variables.** `NOMINATIM_URL` points every geocoding call at a Nominatim of your own, `ALLOW_LINK_LOCAL_IPS` lets a rootless Podman container reach its host gateway, and `AMAP_API_KEY`, `AMAP_API_SECRET` and `AMAP_API_BASE` put Amap into place search for mainland China. All of them are optional. See [Environment-Variables](Environment-Variables).
- **`OVERPASS_TIMEOUT_MS` now defaults to `25000`** instead of `12000`. If you set a lower value by hand, remove it: the query gives Overpass 20 seconds of work, and a shorter timeout hangs up on answers that were still coming.
- **SSO sessions on an OIDC-only instance last 30 days.** The login page sends `remember=1` on that path, so the session gets the `SESSION_DURATION_REMEMBER` lifetime (default `30d`) instead of `SESSION_DURATION` (default `24h`). Lower `SESSION_DURATION_REMEMBER` if that is too long. With password login on, the SSO button follows the Remember-me switch instead. See [OIDC-SSO](OIDC-SSO).
- **Booked nights get a stop.** A migration puts every existing stay that has a place on its check-in day as a hotel stop, so the trips you already have show their hotels in the [Road-Trip](Road-Trip) view without anyone re-saving a booking. Under **Days** nothing changes: that stop stays hidden behind the badge the booking already shows. See [Accommodations](Accommodations).
- **Two things are on by default.** The **Links** tab in Collab, which an admin switches off under **Admin → Addons**, and the routing counters, daily totals of the route requests the planner makes that never leave the instance. An admin reads them at `/api/route-usage/summary`; an `app_settings` row named `route_usage_enabled` with the value `false` switches them off.
## Encryption Key Note
If you are upgrading from a version that predates the dedicated `ENCRYPTION_KEY` (i.e. you have no `ENCRYPTION_KEY` environment variable set), TREK automatically falls back to `./data/.jwt_secret` on startup and immediately promotes it to `./data/.encryption_key`. No manual steps are required — the transition is handled at first boot after the upgrade.
+5 -3
View File
@@ -16,7 +16,7 @@ If your account requires MFA setup, TREK redirects you directly to the **Account
| Appearance | Color mode, color scheme / accent, readability (transparency, reduce motion, density, text size), and which widgets appear on your dashboard | Always |
| Map | Map provider (Leaflet, Mapbox GL, or MapLibre GL), tile presets, map style and Mapbox token, 3D buildings, high-quality mode | Always |
| Notifications | Email, webhook, ntfy, and in-app notification preferences | Always |
| Integrations | Photo providers (Immich, Synology, etc.), the AirTrail connection, your own AI parsing model, and MCP OAuth clients / API tokens | Only when a photo provider (Immich or Synology Photos), MCP, AirTrail or AI Parsing is enabled |
| Integrations | Photo providers (Immich, Synology, etc.), the AirTrail connection, the Dawarich connection, your own AI parsing model, public API keys, and MCP OAuth clients / API tokens | Only when a photo provider (Immich or Synology Photos), MCP, AirTrail, AI Parsing or Dawarich is enabled |
| Plugins | Per-user settings for installed plugins | Only when at least one plugin is installed |
| Offline | Cached trips, pending changes, re-sync and clear cache | Always |
| Account | Username, email, password, MFA (TOTP + backup codes), passkeys, avatar, delete account | Always |
@@ -62,7 +62,7 @@ The Map tab requires an explicit **Save** action after making changes.
**Provider** — choose between:
- **Leaflet** — The default. Pick from built-in presets (OpenStreetMap, OpenStreetMap DE, OpenFreeMap Positron/Bright, CartoDB Light/Dark, Stadia Smooth) or enter a custom tile URL template. OpenFreeMap Positron is what an unconfigured map draws; the CartoDB presets need an API key.
- **Leaflet** — The default. Pick from built-in presets (OpenStreetMap, OpenStreetMap DE, OpenFreeMap Positron/Bright, CartoDB Light/Dark, Stadia Smooth, Amap (高德地图) road and satellite) or enter a custom tile URL template. The Amap presets draw in the offset GCJ-02 datum, which TREK corrects for; see [Map-Settings](Map-Settings). OpenFreeMap Positron is what an unconfigured map draws; the CartoDB presets need an API key.
- **Mapbox GL** (Experimental) — Vector tiles with 3D buildings and terrain. Requires a public Mapbox access token (`pk.*`). Supports built-in style presets (Mapbox Standard, Standard Satellite, Streets, Outdoors, Light, Dark, Satellite, Satellite Streets, Navigation Day, Navigation Night) or a custom `mapbox://styles/USER/ID` URL. Additional options:
- **3D Buildings & Terrain** — Pitch and real 3D building extrusions (works on every style including satellite).
- **High Quality Mode** (Experimental) — Antialiasing + globe projection for sharper edges. May impact performance on lower-end devices.
@@ -91,11 +91,13 @@ If your account was linked via SSO, an **SSO** badge appears next to your role a
## Integrations tab
The Integrations tab is only visible when a **photo provider** (Immich or Synology Photos), **MCP**, **AirTrail** or **AI Parsing** is enabled. Each section then appears with its own addon, so the tab holds only what your instance actually runs:
The Integrations tab is only visible when a **photo provider** (Immich or Synology Photos), **MCP**, **AirTrail**, **AI Parsing** or **Dawarich** is enabled. Each section then appears with its own addon, so the tab holds only what your instance actually runs:
- **Photo Providers** (only when a photo provider is enabled) — Configure Immich, Synology Photos, and other photo integrations. One card appears per enabled provider.
- **AirTrail** (only when the AirTrail addon is enabled) — Instance URL and API key for your self-hosted AirTrail, plus a self-signed-certificate switch and a **Write changes back to AirTrail** toggle (off by default: AirTrail is the source of truth and TREK only reads from it). **Test connection** reports how many flights it can see. The key is stored encrypted and never prefilled — leaving the field blank keeps the stored one.
- **Dawarich** (only when the Dawarich addon is enabled): the **Instance address** and **API key** of your own Dawarich, a self-signed-certificate switch, **Test connection** and **Disconnect**. The key is stored encrypted and never handed back. See [Dawarich](Dawarich).
- **AI parsing** (only when the AI Parsing addon is enabled) — The provider (OpenAI or Anthropic), model and API key used to extract bookings from your uploaded files, plus **Send documents as images** for vision-capable models. This is the per-user fallback: it only takes effect when your administrator has not configured a model for the whole instance. A local Ollama endpoint is not offered here — that is set up once, instance-wide, in the admin settings (#1772). See [AI-Booking-Import](AI-Booking-Import).
- **API Keys** (whenever the tab is shown): read-only keys for the public API, so other software can read your trips. **Create key** names the key and lets you narrow **what this key may read** to single areas; the key is shown once. See [Public-API](Public-API).
- **MCP section** (only when MCP addon is enabled):
- Shows the MCP server endpoint URL.
- **OAuth Clients** sub-tab — Create and manage OAuth 2.1 clients (with redirect URIs and scopes). Quick-fill presets are provided for Claude.ai, Claude Desktop, Cursor, VS Code, Windsurf, and Zed. Active OAuth sessions can be viewed and revoked here.
+1 -1
View File
@@ -6,7 +6,7 @@ The What's Next widget shows the upcoming assigned places across all days of you
## Where to find it
Open the trip planner → **Collab** tab. On **desktop** (window 1024px and wider) the widget appears as a panel alongside Chat, Notes, and Polls. In a narrower window the four panels collapse into a tab bar and it becomes a tab labeled "What's Next". On a **phone** (under 768px) the mobile trip planner takes over, and its Collab tab only offers Chat, Notes and Polls — the widget is not available there. The Collab addon must be enabled and the What's Next sub-feature must be turned on. See [Real-Time-Collaboration](Real-Time-Collaboration).
Open the trip planner → **Collab** tab. On **desktop** (window 1024px and wider) the widget appears as a panel alongside Chat, Notes, Links and Polls. In a narrower window the five panels collapse into a tab bar and it becomes a tab labeled "What's Next". On a **phone** (under 768px) the mobile trip planner takes over, and its Collab tab only offers Chat, Notes, Links and Polls — the widget is not available there. The Collab addon must be enabled and the What's Next sub-feature must be turned on. See [Real-Time-Collaboration](Real-Time-Collaboration).
## What it shows