{"id":48379500,"url":"https://github.com/renji61/remi-trip-planner","last_synced_at":"2026-04-23T13:00:52.722Z","repository":{"id":346508937,"uuid":"1190286066","full_name":"Renji61/remi-trip-planner","owner":"Renji61","description":"Self-hosted trip planner: itinerary, map, budget, stays, rentals, flights, checklist.","archived":false,"fork":false,"pushed_at":"2026-04-15T14:57:59.000Z","size":1925,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-15T16:40:40.797Z","etag":null,"topics":["docker","go","itinerary","itinerary-planner","self-hosted","sqlite","trip-planner"],"latest_commit_sha":null,"homepage":"https://github.com/Renji61/remi-trip-planner","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Renji61.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"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}},"created_at":"2026-03-24T06:15:02.000Z","updated_at":"2026-04-15T14:56:56.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/Renji61/remi-trip-planner","commit_stats":null,"previous_names":["renji61/remi-trip-planner"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/Renji61/remi-trip-planner","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Renji61%2Fremi-trip-planner","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Renji61%2Fremi-trip-planner/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Renji61%2Fremi-trip-planner/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Renji61%2Fremi-trip-planner/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Renji61","download_url":"https://codeload.github.com/Renji61/remi-trip-planner/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Renji61%2Fremi-trip-planner/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32181374,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-23T11:42:27.955Z","status":"ssl_error","status_checked_at":"2026-04-23T11:42:18.877Z","response_time":53,"last_error":"SSL_read: 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":["docker","go","itinerary","itinerary-planner","self-hosted","sqlite","trip-planner"],"created_at":"2026-04-05T19:01:47.823Z","updated_at":"2026-04-23T13:00:52.715Z","avatar_url":"https://github.com/Renji61.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# REMI Trip Planner\n\nA **self-hosted** trip planner: one binary (or container), **SQLite** storage, and a **server-rendered** web UI with light **HTMX** enhancements. It targets **low-end hardware** and simple deployment (Docker optional).\n\n---\n\n## Table of contents\n\n- [Why REMI](#why-remi)\n- [Stack](#stack)\n- [Features](#features)\n- [Repository layout](#repository-layout)\n- [Requirements](#requirements)\n- [Configuration](#configuration)\n- [Run locally](#run-locally)\n- [Docker \u0026 self-hosting](#docker--self-hosting)\n- [Development](#development)\n- [Deployment \u0026 HTTPS](#deployment--https)\n- [PWA \u0026 offline](#pwa--offline)\n- [Sync API](#sync-api)\n- [Backup \u0026 data](#backup--data)\n- [Security notes](#security-notes)\n- [License \u0026 changelog](#license--changelog)\n\n---\n\n## Why REMI\n\n- **Own your data** — SQLite file on disk, no vendor lock-in.\n- **Fast to run** — Go + Chi, minimal JavaScript; suitable for a small VPS or homelab.\n- **Practical trip workflow** — itinerary on a map, expenses vs budget, stays, rentals, flights, and packing-style checklists in one place.\n\n---\n\n## Stack\n\n| Layer | Technology |\n|--------|------------|\n| Language \u0026 HTTP | Go 1.25, [chi](https://github.com/go-chi/chi) router |\n| UI | HTML templates, [HTMX](https://htmx.org/) for partial updates and forms (bundled min script under `web/static/vendor/`, not loaded from a public CDN) |\n| Data | SQLite ([modernc.org/sqlite](https://pkg.go.dev/modernc.org/sqlite)), WAL mode |\n| Maps | [Leaflet](https://leafletjs.com/) + OpenStreetMap / Nominatim (optional geocoding) |\n| Container | Multi-stage Dockerfile, Alpine runtime |\n| Optional TLS | [Caddy](https://caddyserver.com/) example in `deploy/` |\n\n---\n\n## Features\n\n### Trips \u0026 dashboard\n\n- Create trips from the home page (optional **place lookup** for trip map center; falls back to site default location); view **active**, **draft**, and **archived** groups.\n- **First-trip setup** (owners): guided modal for a new trip until onboarding is complete — basics, cover image or preset, currency, and related defaults (`POST /trips/{id}/first-trip-setup`).\n- **Dashboard sidebar \u0026 mobile bottom bar:** up to **two** shortcuts to trips that are **in progress** or **upcoming** (in that priority order), in addition to **My Trip**, **Profile**, and **Settings**.\n- **Dashboard customization:** grid vs list cards, sort order, hero background style, heading text (app settings).\n- Per-trip: name, description, dates, **cover image URL**, **currency**, archive/delete.\n\n### Itinerary \u0026 map\n\n- **Day-grouped** stops with titles, locations, notes, optional cost and times; draft trips without a valid start/end window can still plan by calendar date.\n- **Commute / travel legs** between two itinerary items on the **same day** (`item_kind=commute`): transport mode, ordering, sidebar + timeline + calendar, **ICS** feed entries, and optional **Google Maps directions** when both endpoints have coordinates (`POST /trips/{id}/itinerary/commute`).\n- **Day header map shortcut:** open all non-commute places for a day as a single Google Maps route/search in itinerary order.\n- **Per-day descriptions** (labels) editable inline on the trip page.\n- **Stop weather (optional):** with an **OpenWeatherMap** API key in site settings, plain itinerary stops with coordinates can show a compact **same-day forecast** on the trip page when the day is within the forecast window.\n- **Interactive map** (Leaflet + OpenStreetMap by default, or **Google Maps** when an API key is set) with markers, optional **day filters**, and travel hints between stops; editing a stop **updates stored coordinates** and map pins after save.\n- **Search** across itinerary text from the trip header.\n- **Geocoding** can be disabled globally (app settings) for privacy or rate-limit reasons.\n- **Caching:** server and browser caches reduce repeat geocoding and place-suggestion traffic for the same queries.\n\n### Expenses \u0026 group expenses\n\n- Manual **expenses** (category, amount, date, payment method, notes) at `/trips/{id}/expenses`; **group expenses** at `/trips/{id}/group-expenses` with equal, exact, percent, and share-based splits; **301** redirects from legacy `/budget` and `/tab` URLs.\n- Amounts are persisted as **integer minor units** (e.g. cents) in SQLite for accurate split math; the UI still uses familiar decimal entry.\n- **Optimistic concurrency** on many trip-scoped edits: if someone else saved first, the UI can receive **409 Conflict** with a structured error instead of silently overwriting.\n- **Budget summary** on the trip page (budgeted vs spent), including **Total group expense** on mobile and in the desktop sidebar when group expenses are enabled; dedicated expenses subpage with transactions and export.\n- **Quick expense** entry from the trip sidebar (when the expenses section is enabled) via a **unified form** that can post a **personal** expense or, when group expenses are on and the roster allows it, a **group** split in one flow.\n- **Per-day totals** are shown directly in Expenses and Group expenses day headers for faster scanning.\n- Some expenses are **linked** to stay, vehicle, or flight bookings and edited from those flows.\n- **Departed participants** on group expenses keep historical splits and settlements consistent when collaborators leave; labels show **Left trip** where applicable.\n\n### Stay, vehicle, flights\n\n- Full **accommodation**, **vehicle rental**, and **flights** sections with forms, attachments/images/documents, and links to **itinerary stops** and **expenses** where designed.\n- **Booking status** on each booking type (**booked** vs **to be booked**), and a unified **Booking details** section on the main trip page to filter and review stays, rentals, and flights together.\n- **Flights:** optional **AirLabs** API key enables **airport and airline** field autocomplete via server routes (with caching and fallbacks to existing location providers).\n- **Trip Bookings** checklist lines for flights not yet booked (`Book: …` in a dedicated category), kept in sync with flight fields and marked done when the flight is booked (dismissible).\n- **Add/update/delete** flows for those bookings run in **transactions** with linked itinerary lines (and related cleanup) so you do not get half-applied saves if something fails mid-way.\n\n### Trip documents \u0026 uploads\n\n- **Trip Documents** at `/trips/{id}/documents`: upload general files, search and filter the full attachment list (including files from stays, rentals, flights, and group expenses), rename or delete general uploads, and jump to the source booking or expense where applicable.\n- **App settings:** **Max upload size per file (MB)** (default 5) applies to Trip Documents and attachment fields across trip forms.\n- **Docker:** Compose files mount a **`remi-uploads`** volume at `/app/web/static/uploads` so uploads persist across container rebuilds.\n\n### Checklist\n\n- **Categorized** reminder items; mark done/undo; add from the trip page (including multi-item draft list). **Trip Bookings** lines can track flight booking to-dos linked to the flights list.\n- **Mobile:** **Add to Checklist** appears in the trip **FAB** menu when checklist + sidebar widget visibility allow it (same as trip settings); opens the checklist sheet on the main trip page or via `?open=checklist` from subpages.\n- **Trip FAB flyouts** are shared on trip subpages too (Notes, Expenses, Group expenses, Settings, Trip Documents, Stays, Vehicle Rental, Flights), so add flows no longer require navigating back to the main trip page.\n\n### Notes \u0026 Checklists (account library)\n\n- **Notes \u0026 Checklists** at **`/notes-checklists`**: account-wide **notes** and **checklist templates** with search, archive, and trash — reusable across trips.\n- **Import into a trip** from **Trip Notes** via **`/trips/{id}/notes/import`**: pick global notes and templates to copy into that trip; the server tracks what was already imported per trip.\n\n### Trip page layout \u0026 personalization\n\n- Toggle visibility of **Stay**, **Vehicle**, **Flights**, and **Expenses** (and related nav/widgets).\n- Rename section labels for nav and headings.\n- Control **default expanded** state for itinerary and expense day groups.\n- **12h / 24h** clock and **DD/MM/YYYY** vs **MM/DD/YYYY** calendar date format per trip.\n- **Reorder** main column sections: Trip Map, Itinerary, Expenses, Reminder Checklist, Stay, Vehicle, Flights (hero and trip edit panel stay at the top).\n- **Reorder** right-sidebar widgets: Add New Stop, Total Budgeted Cost, Quick expenses, Add to Checklist (wide layouts; budget/quick respect the expenses section toggle).\n- **Mobile:** the bottom **Trip sections** navigation **scrolls horizontally** when many sections are on, so every tab stays reachable.\n\n### App-wide settings\n\n- App title, default currency, **default map location** (place search with short name stored; Tokyo fallback), map zoom, theme (light / dark / system), location lookup, dashboard presentation options — via **Settings** and quick theme POST from the trip shell.\n- Optional **AirLabs** and **OpenWeatherMap** API keys (same key-edit UX as the Google Maps key) — stored **encrypted** when `REMI_SETTINGS_ENCRYPTION_KEY` is set on the server.\n- **Desktop:** shared **account** dropdown (profile initial, **Profile**, **App settings**, **Log out**) on trip topbars and other app-shell pages for consistent navigation.\n\n### Account export \u0026 privacy\n\n- **Profile → Your data:** `GET /profile/export` (session cookie, same-site browser navigation) downloads **`remi-export-YYYYMMDD.json`**: safe profile fields (no password hash), your user settings, app settings with **secrets redacted** (e.g. Google Maps, AirLabs, and OpenWeatherMap API keys), and every trip you can see (itinerary, expenses, checklist, stays, vehicles, flights, group/tab settlements, guests, departed participants, trip document metadata including stored paths — not file bytes). **CSRF:** export uses **GET** so it is not tied to form CSRF tokens; it only works for an authenticated session from the same site (mitigate cross-site download by keeping cookies `SameSite`/`Secure` in production as configured).\n\n### About \u0026 updates\n\n- **About** page with installed version, bundled changelog excerpt for that version, and **check for updates** for self-hosted installs: the server compares your build to the **newest stable SemVer** found from **GitHub Releases and git tags** (so tag-only publishes still count). Publish **`v*.*.*`** on GitHub for update prompts; **`ahead_of_published`** in the JSON covers preview builds newer than the latest tag.\n\n### PWA\n\n- **Manifest** and **service worker** for add-to-home-screen style use and basic static caching (see [PWA \u0026 offline](#pwa--offline)).\n\n### Sync (local-first / native clients)\n\n- **Read** server change history: `GET /api/v1/trips/{tripID}/changes?since=…` (session cookie, trip access).\n- **Live stream:** `GET /api/v1/trips/{tripID}/events` streams **`text/event-stream`** (change batches) for push-style refresh while a trip is open in the browser.\n- **Write batch:** `POST /api/v1/trips/{tripID}/sync` with JSON body (`client_id`, optional numeric `base_cursor`, `ops[]`) — applies create/update/delete (and trip `archive`) for `trip`, `itinerary_item`, `expense`, `checklist_item`; returns per-op results and new `change_log` rows; conflicts surface as **`conflict`** in per-op results when optimistic locking applies. See [docs/sync_contract.md](docs/sync_contract.md).\n\n---\n\n## Repository layout\n\n```text\ncmd/server/          # HTTP entrypoint\ncmd/dbpeek/          # Optional SQLite debug CLI\ninternal/httpapp/    # Routes, handlers, templates wiring\ninternal/trips/      # Domain types and service logic\ninternal/storage/sqlite/\nmigrations/          # Base schema; extra columns migrated in code\nweb/templates/       # HTML templates\nweb/static/          # CSS, JS, manifest, service worker (uploads at runtime)\ndeploy/              # Caddyfile and remote access notes\ndocs/                # Self-hosting, publish-image, sync contract, CI template, etc.\ndocker-compose.yml   # Build from clone\ndocker-compose.registry.yml   # Pull image (defaults to official GHCR; optional .env)\ndocker-compose.install.yml    # Homelab one-file install (no .env)\n```\n\n---\n\n## Requirements\n\n- **Go 1.25+** (see `go.mod`) for local builds.\n- **Docker** / **Docker Compose** optional, for containerized runs.\n\n---\n\n## Configuration\n\nEnvironment variables (all optional except as noted):\n\n| Variable | Default | Purpose |\n|----------|---------|---------|\n| `APP_ADDR` | `127.0.0.1:4122` | HTTP listen address for **native** runs (loopback only; not reachable from other machines). Set `:4122` to listen on all interfaces. Inside Docker the image uses `:8080`; map host port in Compose (default **4122**). |\n| `SQLITE_PATH` | `./data/trips.db` | SQLite database file path. |\n| `REMI_ROOT` | _(unset)_ | Absolute path to repo root if the process cwd is not the module directory. |\n| `REMI_ENV` | _(unset)_ | Set to **`production`** behind HTTPS: **JSON logs** to stdout, **`Secure` session cookies**, **HSTS** and **CSP-Report-Only** headers, stricter browser defaults. Omit or use any other value for local dev (text logs to stderr). |\n| `REMI_TRUSTED_PROXIES` | _(unset)_ | Comma-separated **trusted proxy IPs or CIDRs** (e.g. `127.0.0.1,10.0.0.0/8`). When the **direct** client IP matches, `X-Forwarded-For` is used for **client IP** (rate limits and access logs). Leave unset if the app is not behind a reverse proxy (avoids spoofing). |\n| `REMI_RATE_LIMIT_AUTH_RPM` | `40` | Per-IP requests per minute for sensitive routes: `POST /login`, `/register`, `/setup`; `GET /verify-email` (with token); `POST /invites/accept`; `POST /profile/resend-verify`. |\n| `REMI_RATE_LIMIT_AUTH_BURST` | `12` | Burst allowance for the same limiter. |\n| `REMI_HSTS_MAX_AGE` | `31536000` | `Strict-Transport-Security` **max-age** (seconds) when `REMI_ENV=production`. Set to `0` to disable HSTS. |\n| `REMI_HEALTHZ_DB` | _(unset)_ | If `1` / `true`, `GET /healthz` also runs **`SELECT 1`** against SQLite (returns **503** if the DB is unreachable). Default health check stays cheap (no DB). |\n| `REMI_ICS_TIMEZONE` | _(unset)_ | **IANA timezone** for the trip **calendar subscription** (`.ics` feed): `DTSTART`/`DTEND` are emitted with `TZID=…` and `X-WR-TIMEZONE` so Google Calendar shows that zone instead of plain UTC. Must match how the server interprets wall times (`time.Local` — usually the same as the `TZ` environment variable). If unset and `TZ` is unset or invalid, the feed uses **UTC (`Z`)** timestamps. Example: `Asia/Kolkata`, `America/New_York`, `Europe/London`. |\n\nInside Docker, the image sets `APP_ADDR=:8080` and `SQLITE_PATH=/app/data/trips.db`.\n\n**Production example (Compose / systemd):** set `REMI_ENV=production`, terminate **TLS** at Caddy/nginx/Cloudflare, and set `REMI_TRUSTED_PROXIES` to your proxy’s **egress** IP(s) toward the app so `X-Forwarded-For` is trusted.\n\n### SQLite concurrency\n\n- SQLite uses **WAL** mode: many **readers** and **one writer** at a time per database file. For internet-facing multi-user use, run **one app instance** (one process/container) writing to a given `SQLITE_PATH` unless you know what you’re doing; **do not** point multiple replicas at the same file on network storage without understanding locking.\n- **Backup:** copy `SQLITE_PATH` and `web/static/uploads/` (or your Docker volumes) on a schedule; test restores.\n\n---\n\n## Run locally\n\n### With Go (no Docker)\n\nFrom the directory that contains `go.mod` (the module root):\n\n```bash\nmkdir -p data\ngo run ./cmd/server\n```\n\nOpen [http://127.0.0.1:4122](http://127.0.0.1:4122) (or [http://localhost:4122](http://localhost:4122) if your system resolves `localhost` to IPv4).\n\nUse another port:\n\n```bash\n# Unix / Git Bash\nAPP_ADDR=:9090 go run ./cmd/server\n```\n\n```powershell\n# Windows PowerShell\n$env:APP_ADDR=\":9090\"; go run ./cmd/server\n```\n\nIf you open the IDE at a **parent** folder (e.g. a folder containing several projects), the server tries to `chdir` into the checkout that contains `module remi-trip-planner` in `go.mod`, or set `REMI_ROOT` explicitly.\n\n### Quality checks\n\n```bash\ngofmt -l .          # should print nothing\ngo vet ./...\ngo test ./...\n```\n\n---\n\n## Docker \u0026 self-hosting\n\n**Official image (public):** `ghcr.io/renji61/remi-trip-planner:latest`  \nVersion pins: `ghcr.io/renji61/remi-trip-planner:v1.50.3` (and other SemVer tags published by CI).\n\n### Quick start — homelab (no `.env`, no git)\n\n```bash\ncurl -fsSL -o docker-compose.yml https://raw.githubusercontent.com/Renji61/remi-trip-planner/main/docker-compose.install.yml\ndocker compose -f docker-compose.yml up -d\n```\n\nOr copy [`docker-compose.install.yml`](docker-compose.install.yml) from this repo and run:\n\n```bash\ndocker compose -f docker-compose.install.yml up -d\n```\n\nOpen [http://localhost:4122](http://localhost:4122). Edit **`4122:8080`** in the file to change the host port (left side = host, right = container).\n\n### Build from clone (developers)\n\n```bash\ncp .env.example .env   # optional: REMI_PORT maps host → container 8080 (default 4122)\ndocker compose up -d --build\n```\n\n- **Data:** named volume **`remi-data`** → `/app/data/trips.db` in the container.\n- **Uploads:** named volume **`remi-uploads`** → `/app/web/static/uploads` (same as registry/install compose variants).\n- **Health:** image includes `wget` and a `HEALTHCHECK` on `GET /healthz` (also declared in Compose).\n- **Hardening (default compose):** app user is non-root; service may use **read-only** root, **`tmpfs`** on `/tmp`, and dropped capabilities — override only if you need extra privileges.\n- **Volume permissions:** the image **`docker-entrypoint.sh`** runs as root on each start, **`chown`s** **`remi-data`** / **`remi-uploads`** mount points for user **`remi`**, then starts the app with **`su-exec`** (so Compose shows one running service). Images **before** this entrypoint may need a one-off manual **`chown`** on those paths.\n- **Manual update (git):** `git pull \u0026\u0026 docker compose up -d --build`\n\n### Registry compose (optional `.env` overrides)\n\n[`docker-compose.registry.yml`](docker-compose.registry.yml) defaults to the same official image and host port **4122** (→ container **8080**) — **no `.env` required**. Set `REMI_IMAGE` or `REMI_PORT` in `.env` only if you fork the image or need another port.\n\nCompose files **do not** include Watchtower or other auto-update sidecars; pull/rebuild when you want a new version, or add your own tooling.\n\nFull instructions: **[docs/self-hosting.md](docs/self-hosting.md)**.\n\nCI: **[.github/workflows/docker-publish.yml](.github/workflows/docker-publish.yml)** pushes `ghcr.io/renji61/remi-trip-planner` on `main` and on SemVer tags `v*.*.*`.\n\n**Publishing / forks:** **[docs/publish-image.md](docs/publish-image.md)** (Actions permissions, PAT `workflow` scope, manual `docker push`).\n\n---\n\n## Development\n\n- **Templates:** `web/templates/*.html` — parsed together; shared fragments live in `partials.html` where used.\n- **Front-end:** `web/static/app.css`, `app.js`, and vendored **`htmx-1.9.12.min.js`**; bump **`RemiStaticAssetVersion`** in **`internal/httpapp/static_asset_version.go`** so templates’ **`{{remiStaticAssetV}}`** query strings refresh browser caches.\n- **Migrations:** new installs run `migrations/001_init.sql`; existing DBs get additive `ALTER TABLE` statements in `internal/storage/sqlite/db.go`.\n- **CI:** copy [docs/github-actions-ci.yml](docs/github-actions-ci.yml) to `.github/workflows/ci.yml` and push (use a Git credential with the **`workflow`** scope if GitHub rejects OAuth pushes to workflow files).\n\n---\n\n## Deployment \u0026 HTTPS\n\n1. Point DNS at your server.\n2. Adjust `deploy/Caddyfile` for your domain.\n3. Run behind Caddy or another reverse proxy with TLS. See [deploy/remote_access.md](deploy/remote_access.md) for notes.\n\n---\n\n## PWA \u0026 offline\n\n- The service worker caches core assets for faster repeat visits; it does **not** replace server data offline.\n- For true offline editing, clients should use the **sync** direction described in [docs/sync_contract.md](docs/sync_contract.md) (work in progress).\n\n---\n\n## Sync API\n\n| Method | Path | Role |\n|--------|------|------|\n| `GET` | `/api/v1/trips/{tripID}/changes` | Paginated-style change log for replication (`since` query supported as implemented). |\n| `POST` | `/api/v1/trips/{tripID}/sync` | Prototype endpoint; request/response evolution documented in `docs/sync_contract.md`. |\n\n---\n\n## Backup \u0026 data\n\n- **SQLite (recommended online backup):** use the SQLite shell so the app can keep running safely (writes a consistent snapshot). Example (adjust paths):\n\n  ```bash\n  mkdir -p ./backup\n  TS=$(date -u +%Y%m%d-%H%M%S)\n  sqlite3 ./data/trips.db \".backup ./backup/remi-trips-${TS}.db\"\n  ```\n\n  On Windows (PowerShell), if `sqlite3` is on your `PATH`:\n\n  ```powershell\n  New-Item -ItemType Directory -Force -Path .\\backup | Out-Null\n  $ts = (Get-Date).ToUniversalTime().ToString(\"yyyyMMdd-HHmmss\")\n  sqlite3 .\\data\\trips.db \".backup '$(Resolve-Path .\\backup)\\remi-trips-$ts.db'\"\n  ```\n\n  Store backups on a **different disk or host** than the live database when possible.\n\n- **Raw file copy:** you can also copy `SQLITE_PATH` while the app is stopped, or copy from the Docker **`remi-data`** volume (see [docs/self-hosting.md](docs/self-hosting.md)).\n- **Uploads:** back up **`web/static/uploads/`** together with the DB (or the Docker **`remi-uploads`** volume / bind mount). Attachments are not inside the SQLite file.\n- **Opt-in scripts:** [scripts/backup-sqlite.sh](scripts/backup-sqlite.sh) and [scripts/backup-sqlite.ps1](scripts/backup-sqlite.ps1); optional [docker-compose.backup.yml](docker-compose.backup.yml) (Compose **profile `backup`**) — wire to **cron** or **Task Scheduler** yourself; nothing runs automatically.\n\n---\n\n## Security notes\n\n- Use **strong passwords**, **HTTPS** in production, and set **`REMI_ENV=production`** so session cookies are **`Secure`** and HSTS/CSP-report-only headers apply. Restrict who can reach the server (VPN, firewall, or authenticated reverse proxy).\n- Configure **`REMI_TRUSTED_PROXIES`** when behind a reverse proxy so rate limits and logs see the real client IP; never trust `X-Forwarded-For` from the open internet without a trusted hop.\n- **Auth audit (structured logs):** successful login, failed login, logout, and password change emit **`slog`** events with **`request_id`**, **client IP** (after trusted-proxy handling), and a **truncated `User-Agent`** — never the password or login identifier.\n- **5xx correlation:** panics and internal server failures use **`writeInternalServerError`**, returning a short public **`error_id`** (HTML page or JSON for `/api/*` / `Accept: application/json`); server logs include the same id plus **`request_id`**, path, method, and **`user_id`** when authenticated.\n- **Auth:** registration errors use a **generic** message when the email/username may already exist, to reduce enumeration.\n- **CSP (report-only):** in production the app sends `Content-Security-Policy-Report-Only`. Tune `connect-src` / `script-src` in `internal/httpapp/routes.go` if your deployment adds other CDNs or APIs, then consider enforcing CSP (drop `Report-Only`) once stable.\n- Keep **secrets** out of git (`.env` is gitignored). Use strong host permissions on the SQLite file.\n\n---\n\n## License \u0026 changelog\n\n- **License:** [MIT](LICENSE).\n- **History:** [CHANGELOG.md](CHANGELOG.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frenji61%2Fremi-trip-planner","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frenji61%2Fremi-trip-planner","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frenji61%2Fremi-trip-planner/lists"}