openvibers/openvibe.tours AGENTS.md

AGENTS.md

main 5.6 KB · 99 lines · markdown

1# AGENTS.md — OpenVibe.Tours
2
3## Project overview
4
5OpenVibe.Tours (`https://openvibe.tours`) is the network's travel room: cities and places from open
6data, IRL live tours and stays from the network's public APIs, self-guided routes with cited stops,
7and a signed-in person's trips. Everything is server-rendered and works without JavaScript.
8
9- **Entry:** `server/index.js`; app in `server/http/router.js`; port `5170`, service id `tours`.
10- **Sources:** `server/sources/` — `osm.js` (Overpass), `wikidata.js`, `wikivoyage.js`/
11 `wikivoyage-client.js`, `live.js` (Live's public list), `rent.js` (Rent's public listings),
12 `openverse.js`, `http.js`.
13- **Places:** `server/places/` — `discover.js`, `is-place.js`, `narrative.js` (AI summary + attribution),
14 `photo-cache.js` (licensed images to Media), `clip.js`.
15- **Jobs:** `server/jobs/` — `cities.js`, `places.js`, `routes.js`, `live.js`, `stays.js`, `index.js`.
16- **API/pages:** `server/http/api.js` (`/api/v1`), `principal.js`, `pages.js`, `discovery.js`;
17 views in `server/views/index.js`/`labels.js`, rendering in `server/render/layout.js`/`html.js`.
18 `public/js/tours.js` is the only own browser script (MapLibre only on the route page); own CSS in
19 `public/css`.
20- **Seeds:** `seed/`, `scripts/seed.js`; status and counts in `STATUS.json`.
21
22## Owns, does not own
23
24Owns its cached open data: `tours_cities` (`cty_`), `tours_city_sources`, `tours_places` (`plc_`),
25`tours_routes` (`rte_`), `tours_route_stops`, the trips/shares tables and its outbox. Every row keeps
26the source URL and the time fetched.
27
28Does not own: Live, Rent, Media and AI — Tours reads Live's and Rent's public pages/lists; licensed
29place images are copied to Media with the `media.object.upload` service scope; sourced summaries use
30AI (`ai.run.create`, `ai.run.read`) when `OV_AI_INTERNAL_URL` is configured. Both keep the original
31source and attribution on the place page. Identity/roles are Network's (OAuth client `tours`, PKCE
32S256, canonical `usr_…`). OpenStreetMap, Wikivoyage, Wikidata and Commons are external open data,
33cached with source and fetched time — never scraped past their terms. Tours never takes a payment and
34carries **no affiliate link**.
35
36## Who calls it, what it calls
37
38People use the pages/API with the `tours_at` session cookie or a Network Bearer token. Trips and saved
39places are the person's own; a share link is read-only. Tours calls Network (SSO, JWKS, identity),
40Live's public stream/VOD list, Rent's public listings, Media (image upload), AI (summaries when
41configured), and the open-data endpoints (Wikidata, Wikivoyage, Overpass, Commons, Openverse) through
42`server/sources/`. It calls no service with a person's write; the public reads are cacheable, a person's
43page is `private, no-store`.
44
45## Database and migrations
46
47PostgreSQL through `openvibe-sdk/db`; migrations `migrations/0001..0011` (initial, place sources,
48search index, English names, shares/defaults, outbox, discoverable/notable places, photo retries) are
49applied at boot with the owner role. Tests use PGlite; `npm run test:pg` runs them on PostgreSQL.
50Never edit a migration that has run. A job records what it fetched; a failed source leaves the cached
51row and its fetched time rather than inventing content.
52
53## Auth
54
55Network SSO (RS256 JWT + `tours_at` cookie) and offline JWKS verification. A write made with the
56session cookie must come from `https://openvibe.tours`, else `403 tours.cross_site`. Every person
57acts only on their own trips and saved places; a share link is an unguessable read-only token.
58`/internal/events` is HMAC-verified and loopback-only.
59
60## Deploy
61
62`deploy/systemd/openvibe-tours.service` (port `5170`, env `/etc/openvibe/tours.env`, checkout
63`/opt/openvibe.tours`) and `deploy/nginx/openvibe.tours.conf` (the forms zone covers `/trips`);
64`sudo ovhost deploy tours` is the production path and `sudo ovhost rollback tours --to <sha>` the
65rollback.
66
67## Commands and tests
68
69```bash
70npm ci
71npm run dev # http://127.0.0.1:5170
72npm test # every test/*.test.js, PGlite + in-process mocks
73npm test -- places routes # only matching files
74npm run test:pg # the same on PostgreSQL
75npm run seed # seed cities/places from the open sources
76```
77
78`test/run.js` uses `openvibe-shared/test-runner` (90 s, serial); temp PGlite plus in-process Network,
79Live, Chat, Today and OSM/Ticketmaster fixtures, never the network. Tests cover the sources and
80parsers, place discovery, routes and stops, trips/shares, the pages, accent contrast, account data,
81agent twins and discovery artifacts.
82
83## Conventions and pitfalls
84
85- CommonJS, 4-space indent, single quotes, semicolons; problem+json with a stable code. No linter.
86- **Every fact carries its source and fetched time** (city, place, route, stop, photo licence). A
87 missing or failed source is shown honestly; never invent a place, a date, an opening time or a route.
88- **No affiliate and no payment.** Flights are a link to the official search; v1 had no flight API key.
89 The Duffel/Kiwi seam stays off, and a key alone does not turn it on.
90- **TTS is off** until AI has a speech route: every stop says so instead of shipping a dead player.
91- **Attribution is part of the data:** copying a photo to Media or summarising with AI keeps the
92 original source and licence on the place page.
93- **Routes are generated and marked generated**, every stop cited; the no-JavaScript version is a
94 complete SVG + ordered list, not an empty map.
95- **Respect the open-data terms:** no bulk scraping past Overpass/Wikivoyage/Wikidata/Commons limits;
96 use the cached row while a source is unavailable.
97- New env names go into `.env.example` and the README; secret values live only in
98 `/etc/openvibe/tours.env`.
99