A journey day now has a list button in its header beside the route, with the number of stays still waiting, and the row of the day's Dawarich stays gets a "Take over day" button; on a phone the journey's More options offers it for the day of the card shown. The dialog reads the day again from the caller's own Dawarich and shows the route and the stays on one map, numbered in the order they were lived and pinned in the colour of the day, beside a list with times, durations, the stays Dawarich only guessed or is unsure about, the ones without a position, the entries the journal has on the day and how many people will see the drafts. When Dawarich does not answer, it says why and shows the stays of the last sync. Open stays start chosen, the same place twice in a row starts joined as one entry under a title and a chosen map position, any two neighbours can be joined or kept apart, a name can be corrected and a stay left out. "Day report" turns every chosen stay into one entry that lists them with their times, at a chosen stay or off the map. The day's route can go along when the journey keeps none yet. Nothing is written before "Take over N drafts", and a blank name or title holds it back with the reason. Stays left out are dismissed and can be restored in the stay list. The take-over writes drafts only, never touches what the journal holds, and is one transaction: every stay is checked to be the caller's and of that day, the entries, the stays they hold and the marked suggestions commit together, and the entries are announced after the commit. A stay the journey holds already is reported instead of written twice, so a retry after a lost answer is harmless; a failed attempt reads the day again and keeps the choices, and a route that fails after the drafts can be kept again alone. Server: migration 046170 adds journey_entry_stays (one row per stay of an entry with its own name, place and times, unique per journey, source, person and visit, gone with its entry), owned by the journey domain through JourneyEntryStaysService. The single stay accept records its stay there too, over REST and MCP alike. DawarichSyncService.refreshDay reads one day's visits and upserts them without flagging anything missing; DawarichDayStaysService lists and settles a day's stays. JourneyDayImportService in journey-recordings serves POST /api/journeys/:id/recordings/:date/stays/review and /import with @trek/shared contracts, and the MCP tools review_journey_day_stays and take_over_journey_day_stays run on the same service and checks. createEntry can write without announcing for a caller that announces after its commit. The desktop dialog and the phone sheet of the route (JRN-01) and of the take-over share one frame each. All 27 locales carry the strings; the Journey and Dawarich wiki pages and the MCP tool list describe it.
A self-hosted, real-time collaborative travel planner — with maps, budgets, packing lists, a journal, and AI built in.
What you get
See all features
Most of what follows is an addon an admin switches on or off. Lists, Costs, Documents, Collab, Vacay and Atlas ship on; Journey, Collections, MCP, AI Parsing and AirTrail ship off and are marked below.
🧭 Planning
|
🧳 Bookings and money
|
👥 Collaboration
📔 Journal, Atlas and Vacay
|
🧩 Plugins
🤖 AI and MCP
|
📱 Mobile and offline
|
⚙️ Admin, accounts and security
|
AI usage
We use LLM-assisted coding tools across parts of this codebase. Nothing ships that a maintainer has not read and understood: every change goes through a pull request, is reviewed and tested, and has a human who can answer for it. "The AI wrote that" is not an answer any of us would accept from ourselves.
See How we use AI in TREK for the details.
Get started in 30 seconds
ENCRYPTION_KEY=$(openssl rand -hex 32) docker run -d -p 3000:3000 \
-e ENCRYPTION_KEY=$ENCRYPTION_KEY \
-v ./data:/app/data -v ./uploads:/app/uploads mauriceboe/trek
Open http://localhost:3000. On first boot TREK seeds an admin account — if you set ADMIN_EMAIL/ADMIN_PASSWORD those are used, otherwise the credentials are printed to the container log (docker logs trek).
Tech stack
Real-time sync via WebSocket (ws). Backend on NestJS 11. State with Zustand. Auth via JWT + OAuth 2.1 + OIDC + Passkeys (WebAuthn) + TOTP MFA. Weather via Open-Meteo (no key required). Maps with Leaflet and Mapbox GL.
Docker Compose (production)
The repository ships a ready-to-use docker-compose.yml
with secure defaults and every option documented inline. Download it, then:
docker compose up -d
See Install with Docker Compose for the full walkthrough.
Helm (Kubernetes)
helm repo add trek https://chart.liketrek.com
helm repo update
helm install trek trek/trek
See charts/README.md for values.
Install as App (PWA)
TREK works as a Progressive Web App — no App Store needed.
- Open TREK in the browser (HTTPS required)
- iOS: Share ▸ Add to Home Screen
- Android: Menu ▸ Install app (or Add to Home Screen)
TREK then launches fullscreen with its own icon, just like a native app.
Updating
See Updating — Docker Compose, Docker run, Helm, Portainer, Unraid and Proxmox, plus the encryption-key note.
Reverse Proxy
For production, put TREK behind a TLS-terminating reverse proxy. TREK uses WebSockets for real-time sync, so the proxy must support WebSocket upgrades on /ws.
If you use the MCP addon, the proxy must also pass the Mcp-Session-Id header through in both directions on /mcp — Nginx and Caddy do this by default, but a proxy that strips it makes every tool call open a new session instead of reusing one. See the Reverse Proxy wiki page for details.
Nginx
server {
listen 80;
server_name trek.yourdomain.com;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name trek.yourdomain.com;
ssl_certificate /etc/ssl/fullchain.pem;
ssl_certificate_key /etc/ssl/privkey.pem;
# 500 MB covers backup-restore uploads (capped at 500 MB server-side).
client_max_body_size 500m;
location / {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /ws {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
# Only needed if you use the MCP addon. Responses are Server-Sent Events,
# so buffering must be off or tool results arrive late.
location /mcp {
proxy_pass http://localhost:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_buffering off;
proxy_read_timeout 3600s;
}
}
Caddy
trek.yourdomain.com {
reverse_proxy localhost:3000
}
Caddy handles TLS and WebSockets automatically.
Environment variables
Every variable, its default and what it does: see Environment Variables.
Star History
Data sources
The Atlas map's country and sub-national (province/county) boundaries come from geoBoundaries (Runfola et al., 2020), licensed CC BY 4.0. See NOTICE.md for full third-party attributions.
License
TREK is AGPL v3. Self-host freely for personal or internal company use. If you modify and offer TREK as a network service to third parties, your modifications must be open-sourced under the same licence.



