{"id":48369205,"url":"https://github.com/mauriceboe/TREK","last_synced_at":"2026-04-08T21:01:22.866Z","repository":{"id":345400492,"uuid":"1186219527","full_name":"mauriceboe/TREK","owner":"mauriceboe","description":"A self-hosted travel/trip planner with real-time collaboration, interactive maps, PWA support, SSO, budgets, packing lists, and more.","archived":false,"fork":false,"pushed_at":"2026-04-05T21:26:47.000Z","size":18037,"stargazers_count":3696,"open_issues_count":5,"forks_count":325,"subscribers_count":9,"default_branch":"main","last_synced_at":"2026-04-05T21:30:28.708Z","etag":null,"topics":["budget-tracker","collaborative","open-source","opensource","packing-list","poi","real-time","routes","self-hosted","travel","travel-app","travel-planner","traveling","trip","trip-planner","wanderlog","wanderlust","webapplication"],"latest_commit_sha":null,"homepage":"https://demo-nomad.pakulat.org","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mauriceboe.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"ko_fi":"mauriceboe","buy_me_a_coffee":"mauriceboe"}},"created_at":"2026-03-19T11:51:54.000Z","updated_at":"2026-04-05T21:26:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mauriceboe/TREK","commit_stats":null,"previous_names":["mauriceboe/nomad","mauriceboe/trek"],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/mauriceboe/TREK","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mauriceboe%2FTREK","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mauriceboe%2FTREK/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mauriceboe%2FTREK/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mauriceboe%2FTREK/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mauriceboe","download_url":"https://codeload.github.com/mauriceboe/TREK/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mauriceboe%2FTREK/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31573788,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"ssl_error","status_checked_at":"2026-04-08T14:31:17.202Z","response_time":54,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["budget-tracker","collaborative","open-source","opensource","packing-list","poi","real-time","routes","self-hosted","travel","travel-app","travel-planner","traveling","trip","trip-planner","wanderlog","wanderlust","webapplication"],"created_at":"2026-04-05T16:00:25.980Z","updated_at":"2026-04-08T21:01:22.842Z","avatar_url":"https://github.com/mauriceboe.png","language":"TypeScript","readme":"\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"client/public/logo-light.svg\" /\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"client/public/logo-dark.svg\" /\u003e\n    \u003cimg src=\"client/public/logo-light.svg\" alt=\"TREK\" height=\"60\" /\u003e\n  \u003c/picture\u003e\n  \u003cbr /\u003e\n  \u003cem\u003eYour Trips. Your Plan.\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://discord.gg/J27gr9GH\"\u003e\u003cimg src=\"https://img.shields.io/badge/Discord-Join%20Community-5865F2?logo=discord\u0026logoColor=white\" alt=\"Discord\" /\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/License-AGPL_v3-blue.svg\" alt=\"License: AGPL v3\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://hub.docker.com/r/mauriceboe/trek\"\u003e\u003cimg src=\"https://img.shields.io/docker/pulls/mauriceboe/trek\" alt=\"Docker Pulls\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/mauriceboe/TREK\"\u003e\u003cimg src=\"https://img.shields.io/github/stars/mauriceboe/TREK\" alt=\"GitHub Stars\" /\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/mauriceboe/TREK/commits\"\u003e\u003cimg src=\"https://img.shields.io/github/last-commit/mauriceboe/TREK\" alt=\"Last Commit\" /\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  A self-hosted, real-time collaborative travel planner with interactive maps, budgets, packing lists, and more.\n  \u003cbr /\u003e\n  \u003cstrong\u003e\u003ca href=\"https://demo-nomad.pakulat.org\"\u003eLive Demo\u003c/a\u003e\u003c/strong\u003e — Try TREK without installing. Resets hourly.\n\u003c/p\u003e\n\n![TREK Screenshot](docs/screenshot.png)\n![TREK Screenshot 2](docs/screenshot-2.png)\n\n\u003cdetails\u003e\n\u003csummary\u003eMore Screenshots\u003c/summary\u003e\n\n| | |\n|---|---|\n| ![Plan Detail](docs/screenshot-plan-detail.png) | ![Bookings](docs/screenshot-bookings.png) |\n| ![Budget](docs/screenshot-budget.png) | ![Packing List](docs/screenshot-packing.png) |\n| ![Files](docs/screenshot-files.png) | |\n\n\u003c/details\u003e\n\n## Features\n\n### Trip Planning\n- **Drag \u0026 Drop Planner** — Organize places into day plans with reordering and cross-day moves\n- **Interactive Map** — Leaflet map with photo markers, clustering, route visualization, and customizable tile sources\n- **Place Search** — Search via Google Places (with photos, ratings, opening hours) or OpenStreetMap (free, no API key needed)\n- **Day Notes** — Add timestamped, icon-tagged notes to individual days with drag \u0026 drop reordering\n- **Route Optimization** — Auto-optimize place order and export to Google Maps\n- **Weather Forecasts** — 16-day forecasts via Open-Meteo (no API key needed) with historical climate averages as fallback\n- **Map Category Filter** — Filter places by category and see only matching pins on the map\n\n### Travel Management\n- **Reservations \u0026 Bookings** — Track flights, accommodations, restaurants with status, confirmation numbers, and file attachments\n- **Budget Tracking** — Category-based expenses with pie chart, per-person/per-day splitting, and multi-currency support\n- **Packing Lists** — Category-based checklists with user assignment, packing templates, and progress tracking\n- **Packing Templates** — Create reusable packing templates in the admin panel with categories and items, apply to any trip\n- **Bag Tracking** — Optional weight tracking and bag assignment for packing items with iOS-style weight distribution (admin-toggleable)\n- **Document Manager** — Attach documents, tickets, and PDFs to trips, places, or reservations (up to 50 MB per file)\n- **PDF Export** — Export complete trip plans as PDF with cover page, images, notes, and TREK branding\n\n### Mobile \u0026 PWA\n- **Progressive Web App** — Install on iOS and Android directly from the browser, no App Store needed\n- **Offline Support** — Service Worker caches map tiles, API data, uploads, and static assets via Workbox\n- **Native App Feel** — Fullscreen standalone mode, custom app icon, themed status bar, and splash screen\n- **Touch Optimized** — Responsive design with mobile-specific layouts, touch-friendly controls, and safe area handling\n\n### Collaboration\n- **Real-Time Sync** — Plan together via WebSocket — changes appear instantly across all connected users\n- **Multi-User** — Invite members to collaborate on shared trips with role-based access\n- **Invite Links** — Create one-time registration links with configurable max uses and expiry for easy onboarding\n- **Single Sign-On (OIDC)** — Login with Google, Apple, Authentik, Keycloak, or any OIDC provider\n- **Two-Factor Authentication (MFA)** — TOTP-based 2FA with QR code setup, works with Google Authenticator, Authy, etc.\n- **Collab** — Chat with your group, share notes, create polls, and track who's signed up for each day's activities\n\n### Addons (modular, admin-toggleable)\n- **Vacay** — Personal vacation day planner with calendar view, public holidays (100+ countries), company holidays, user fusion with live sync, and carry-over tracking\n- **Atlas** — Interactive world map with visited countries, bucket list with planned travel dates, travel stats, continent breakdown, streak tracking, and liquid glass UI effects\n- **Collab** — Chat with your group, share notes, create polls, and track who's signed up for each day's activities\n- **Dashboard Widgets** — Currency converter and timezone clock, toggleable per user\n\n### Customization \u0026 Admin\n- **Dashboard Views** — Toggle between card grid and compact list view on the My Trips page\n- **Dark Mode** — Full light and dark theme with dynamic status bar color matching\n- **Multilingual** — English, German, Spanish, French, Russian, Chinese (Simplified), Dutch, Arabic (with RTL support)\n- **Admin Panel** — User management, invite links, packing templates, global categories, addon management, API keys, backups, and GitHub release history\n- **Auto-Backups** — Scheduled backups with configurable interval and retention\n- **Customizable** — Temperature units, time format (12h/24h), map tile sources, default coordinates\n\n## Tech Stack\n\n- **Backend**: Node.js 22 + Express + SQLite (`better-sqlite3`)\n- **Frontend**: React 18 + Vite + Tailwind CSS\n- **PWA**: vite-plugin-pwa + Workbox\n- **Real-Time**: WebSocket (`ws`)\n- **State**: Zustand\n- **Auth**: JWT + OIDC + TOTP (MFA)\n- **Maps**: Leaflet + react-leaflet-cluster + Google Places API (optional)\n- **Weather**: Open-Meteo API (free, no key required)\n- **Icons**: lucide-react\n\n## Quick Start\n\n```bash\nENCRYPTION_KEY=$(openssl rand -hex 32) docker run -d -p 3000:3000 \\\n  -e ENCRYPTION_KEY=$ENCRYPTION_KEY \\\n  -v ./data:/app/data -v ./uploads:/app/uploads mauriceboe/trek\n```\n\nThe app runs on port `3000`. The first user to register becomes the admin.\n\n### Install as App (PWA)\n\nTREK works as a Progressive Web App — no App Store needed:\n\n1. Open your TREK instance in the browser (HTTPS required)\n2. **iOS**: Share button → \"Add to Home Screen\"\n3. **Android**: Menu → \"Install app\" or \"Add to Home Screen\"\n4. TREK launches fullscreen with its own icon, just like a native app\n\n\u003cdetails\u003e\n\u003csummary\u003eDocker Compose (recommended for production)\u003c/summary\u003e\n\n```yaml\nservices:\n  app:\n    image: mauriceboe/trek:latest\n    container_name: trek\n    read_only: true\n    security_opt:\n      - no-new-privileges:true\n    cap_drop:\n      - ALL\n    cap_add:\n      - CHOWN\n      - SETUID\n      - SETGID\n    tmpfs:\n      - /tmp:noexec,nosuid,size=64m\n    ports:\n      - \"3000:3000\"\n    environment:\n      - NODE_ENV=production\n      - PORT=3000\n      - ENCRYPTION_KEY=${ENCRYPTION_KEY:-} # Recommended. Generate with: openssl rand -hex 32. If unset, falls back to data/.jwt_secret (existing installs) or auto-generates a key (fresh installs).\n      - TZ=${TZ:-UTC} # Timezone for logs, reminders and scheduled tasks (e.g. Europe/Berlin)\n      - LOG_LEVEL=${LOG_LEVEL:-info} # info = concise user actions; debug = verbose admin-level details\n      - ALLOWED_ORIGINS=${ALLOWED_ORIGINS:-} # Comma-separated origins for CORS and email notification links\n      - FORCE_HTTPS=true # Redirect HTTP to HTTPS when behind a TLS-terminating proxy\n      # - COOKIE_SECURE=false # Uncomment if accessing over plain HTTP (no HTTPS). Not recommended for production.\n      - TRUST_PROXY=1 # Number of trusted proxies for X-Forwarded-For\n      # - ALLOW_INTERNAL_NETWORK=true # Uncomment if Immich or other services are on your local network (RFC-1918 IPs)\n      - APP_URL=${APP_URL:-} # Base URL of this instance — required when OIDC is enabled; must match the redirect URI registered with your IdP; Also used as the base URL for email notifications and other external links\n      # - OIDC_ISSUER=https://auth.example.com # OpenID Connect provider URL\n      # - OIDC_CLIENT_ID=trek # OpenID Connect client ID\n      # - OIDC_CLIENT_SECRET=supersecret # OpenID Connect client secret\n      # - OIDC_DISPLAY_NAME=SSO # Label shown on the SSO login button\n      # - OIDC_ONLY=false # Set to true to disable local password auth entirely (SSO only)\n      # - OIDC_ADMIN_CLAIM=groups # OIDC claim used to identify admin users\n      # - OIDC_ADMIN_VALUE=app-trek-admins # Value of the OIDC claim that grants admin role\n      # - OIDC_SCOPE=openid email profile # Fully overrides the default. Add extra scopes as needed (e.g. add groups if using OIDC_ADMIN_CLAIM)\n      # - OIDC_DISCOVERY_URL= # Override the OIDC discovery endpoint for providers with non-standard paths (e.g. Authentik)\n      # - DEMO_MODE=false # Enable demo mode (resets data hourly)\n      # - ADMIN_EMAIL=admin@trek.local # Initial admin e-mail — only used on first boot when no users exist\n      # - ADMIN_PASSWORD=changeme      # Initial admin password — only used on first boot when no users exist\n      # - MCP_RATE_LIMIT=60 # Max MCP API requests per user per minute (default: 60)\n      # - MCP_MAX_SESSION_PER_USER=5 # Max concurrent MCP sessions per user (default: 5)\n    volumes:\n      - ./data:/app/data\n      - ./uploads:/app/uploads\n    restart: unless-stopped\n    healthcheck:\n      test: [\"CMD\", \"wget\", \"-qO-\", \"http://localhost:3000/api/health\"]\n      interval: 30s\n      timeout: 10s\n      retries: 3\n      start_period: 15s\n```\n\n```bash\ndocker compose up -d\n```\n\n\u003c/details\u003e\n\n### Updating\n\n**Docker Compose** (recommended):\n\n```bash\ndocker compose pull \u0026\u0026 docker compose up -d\n```\n\n**Docker Run** — use the same volume paths from your original `docker run` command:\n\n```bash\ndocker pull mauriceboe/trek\ndocker rm -f trek\ndocker run -d --name trek -p 3000:3000 -v ./data:/app/data -v ./uploads:/app/uploads --restart unless-stopped mauriceboe/trek\n```\n\n\u003e **Tip:** Not sure which paths you used? Run `docker inspect trek --format '{{json .Mounts}}'` before removing the container.\n\nYour data is persisted in the mounted `data` and `uploads` volumes — updates never touch your existing data.\n\n### Rotating the Encryption Key\n\nIf you need to rotate `ENCRYPTION_KEY` (e.g. you are upgrading from a version that derived encryption from `JWT_SECRET`), use the migration script to re-encrypt all stored secrets under the new key without starting the app:\n\n```bash\ndocker exec -it trek node --import tsx scripts/migrate-encryption.ts\n```\n\nThe script will prompt for your old and new keys interactively (input is not echoed). It creates a timestamped database backup before making any changes and exits with a non-zero code if anything fails.\n\n**Upgrading from a previous version?** Your old JWT secret is in `./data/.jwt_secret`. Use its contents as the \"old key\" and your new `ENCRYPTION_KEY` value as the \"new key\".\n\n### Reverse Proxy (recommended)\n\nFor production, put TREK behind a reverse proxy with HTTPS (e.g. Nginx, Caddy, Traefik).\n\n\u003e **Important:** TREK uses WebSockets for real-time sync. Your reverse proxy must support WebSocket upgrades on the `/ws` path.\n\n\u003cdetails\u003e\n\u003csummary\u003eNginx\u003c/summary\u003e\n\n```nginx\nserver {\n    listen 80;\n    server_name trek.yourdomain.com;\n    return 301 https://$host$request_uri;\n}\n\nserver {\n    listen 443 ssl http2;\n    server_name trek.yourdomain.com;\n\n    ssl_certificate /path/to/fullchain.pem;\n    ssl_certificate_key /path/to/privkey.pem;\n\n    location /ws {\n        proxy_pass http://localhost:3000;\n        proxy_http_version 1.1;\n        proxy_set_header Upgrade $http_upgrade;\n        proxy_set_header Connection \"upgrade\";\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n        proxy_read_timeout 86400;\n    }\n\n    location / {\n        proxy_pass http://localhost:3000;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eCaddy\u003c/summary\u003e\n\nCaddy handles WebSocket upgrades automatically:\n\n```\ntrek.yourdomain.com {\n    reverse_proxy localhost:3000\n}\n```\n\n\u003c/details\u003e\n\n## Environment Variables\n\n| Variable | Description | Default |\n|----------|-------------|---------|\n| **Core** | | |\n| `PORT` | Server port | `3000` |\n| `NODE_ENV` | Environment (`production` / `development`) | `production` |\n| `ENCRYPTION_KEY` | At-rest encryption key for stored secrets (API keys, MFA, SMTP, OIDC). Recommended: generate with `openssl rand -hex 32`. If unset, falls back to `data/.jwt_secret` (existing installs) or auto-generates a key (fresh installs). | Auto |\n| `TZ` | Timezone for logs, reminders and cron jobs (e.g. `Europe/Berlin`) | `UTC` |\n| `LOG_LEVEL` | `info` = concise user actions, `debug` = verbose details | `info` |\n| `ALLOWED_ORIGINS` | Comma-separated origins for CORS and email links | same-origin |\n| `FORCE_HTTPS` | Redirect HTTP to HTTPS behind a TLS-terminating proxy | `false` |\n| `COOKIE_SECURE` | Set to `false` to allow session cookies over plain HTTP (e.g. accessing via IP without HTTPS). Defaults to `true` in production. **Not recommended to disable in production.** | `true` |\n| `TRUST_PROXY` | Number of trusted reverse proxies for `X-Forwarded-For` | `1` |\n| `ALLOW_INTERNAL_NETWORK` | Allow outbound requests to private/RFC-1918 IP addresses. Set to `true` if Immich or other integrated services are hosted on your local network. Loopback (`127.x`) and link-local/metadata addresses (`169.254.x`) are always blocked regardless of this setting. | `false` |\n| `APP_URL` | Public base URL of this instance (e.g. `https://trek.example.com`). Required when OIDC is enabled — must match the redirect URI registered with your IdP. Also used as the base URL for external links in email notifications. | — |\n| **OIDC / SSO** | | |\n| `OIDC_ISSUER` | OpenID Connect provider URL | — |\n| `OIDC_CLIENT_ID` | OIDC client ID | — |\n| `OIDC_CLIENT_SECRET` | OIDC client secret | — |\n| `OIDC_DISPLAY_NAME` | Label shown on the SSO login button | `SSO` |\n| `OIDC_ONLY` | Disable local password auth entirely (first SSO login becomes admin) | `false` |\n| `OIDC_ADMIN_CLAIM` | OIDC claim used to identify admin users | — |\n| `OIDC_ADMIN_VALUE` | Value of the OIDC claim that grants admin role | — |\n| `OIDC_SCOPE` | Space-separated OIDC scopes to request. **Fully replaces** the default — always include `openid email profile` plus any extra scopes you need (e.g. add `groups` when using `OIDC_ADMIN_CLAIM`) | `openid email profile` |\n| `OIDC_DISCOVERY_URL` | Override the auto-constructed OIDC discovery endpoint. Useful for providers that expose it at a non-standard path (e.g. Authentik: `https://auth.example.com/application/o/trek/.well-known/openid-configuration`) | — |\n| **Initial Setup** | | |\n| `ADMIN_EMAIL` | Email for the first admin account created on initial boot. Must be set together with `ADMIN_PASSWORD`. If either is omitted a random password is generated and printed to the server log. Has no effect once any user exists. | `admin@trek.local` |\n| `ADMIN_PASSWORD` | Password for the first admin account created on initial boot. Must be set together with `ADMIN_EMAIL`. | random |\n| **Other** | | |\n| `DEMO_MODE` | Enable demo mode (hourly data resets) | `false` |\n| `MCP_RATE_LIMIT` | Max MCP API requests per user per minute | `60` |\n| `MCP_MAX_SESSION_PER_USER` | Max concurrent MCP sessions per user | `5` |\n\n## Optional API Keys\n\nAPI keys are configured in the **Admin Panel** after login. Keys set by the admin are automatically shared with all users — no per-user configuration needed.\n\n### Google Maps (Place Search \u0026 Photos)\n\n1. Go to [Google Cloud Console](https://console.cloud.google.com/)\n2. Create a project and enable the **Places API (New)**\n3. Create an API key under Credentials\n4. In TREK: Admin Panel → Settings → Google Maps\n\n## Building from Source\n\n```bash\ngit clone https://github.com/mauriceboe/TREK.git\ncd TREK\ndocker build -t trek .\n```\n\n## Data \u0026 Backups\n\n- **Database**: SQLite, stored in `./data/travel.db`\n- **Uploads**: Stored in `./uploads/`\n- **Logs**: `./data/logs/trek.log` (auto-rotated)\n- **Backups**: Create and restore via Admin Panel\n- **Auto-Backups**: Configurable schedule and retention in Admin Panel\n\n## License\n\n[AGPL-3.0](LICENSE)\n","funding_links":["https://ko-fi.com/mauriceboe","https://buymeacoffee.com/mauriceboe"],"categories":["TypeScript","open-source"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmauriceboe%2FTREK","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmauriceboe%2FTREK","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmauriceboe%2FTREK/lists"}