# MultiTrain Search train connections from **multiple start stations to multiple destination stations** in one query. Results are merged, deduplicated, and ranked; each station can carry a **penalty in minutes** (e.g. "Wien Meidling +10 because it takes me longer to get there") that is added to a journey's duration for ranking only — displayed times stay real. Data comes from [Transitous](https://transitous.org), a community-run [MOTIS](https://github.com/motis-project/motis) instance aggregating open timetable data from many carriers (ÖBB, DB, and many more). Please respect their fair-use policy: set a `USER_AGENT` that identifies you. ## Development ```bash nix develop # provides node 22 + npm + typescript-language-server npm install npm run dev # dev server on http://127.0.0.1:3000 ``` Note: `tsx watch` does not detect file changes on Windows-mounted drives (`/mnt/*` in WSL) — restart manually there. ## Architecture Lightweight hexagonal: `src/model` is the pure domain core (domain model, `JourneySource` port, search use case — fan-out over station pairs, merge, dedupe, penalty scoring). `src/adapter` holds the Fastify HTTP adapter and the Transitous adapter (the only place touching the MOTIS client libraries). `src/main.ts` wires everything by hand. ## API - `GET /api/locations?query=wien` — station suggestions - `POST /api/search` — body: ```json { "from": [{ "query": "Wien Hbf", "penalty": 0 }, { "query": "Wien Meidling", "penalty": 10 }], "to": [{ "query": "München Hbf", "penalty": 0 }], "departure": "2026-07-11T08:00:00Z" } ``` `departure` and `arrival` are mutually exclusive; omit both to depart now. ## Configuration (environment) | Variable | Default | Purpose | |---|---|---| | `PORT` | `3000` | HTTP port | | `HOST` | `127.0.0.1` | Bind address | | `USER_AGENT` | `multitrain` | Sent to the MOTIS API — please personalize | | `MOTIS_BASE_URL` | api.transitous.org | Self-hosted MOTIS instance | ## Deployment on NixOS ```nix # flake inputs inputs.multitrain.url = "github:you/multitrain"; # or a path/git url # NixOS configuration { imports = [ inputs.multitrain.nixosModules.default ]; services.multitrain = { enable = true; port = 3000; host = "127.0.0.1"; userAgent = "multitrain (you@example.org)"; # motisBaseUrl = "https://motis.example.org"; }; } ``` `nix build` produces the server as `result/bin/multitrain`. After changing `package-lock.json`, refresh `npmDepsHash` in `flake.nix` (`nix run nixpkgs#prefetch-npm-deps -- package-lock.json`).