openvibers/openvibe.surf AGENTS.md

AGENTS.md

main 5.6 KB · 95 lines · markdown

1# AGENTS.md — OpenVibe.Surf
2
3## Project overview
4
5OpenVibe.Surf (`https://openvibe.surf`) is one room with three surfs:
6
7- **Ride** — CS-style surf maps played inside OpenVibe.Games embeds; the catalogue is proxied from Games.
8- **Watch** — lean-back channel surfing over OpenVibe.Live: live streams, clips and AI moments merged into
9 one feed, plus in-memory watch-together parties over SSE.
10- **Web** — *Surf with Vibe*: a standing brief sent to OpenVibe.Actor so an agent browses for the person
11 and returns a cited report.
12
13- **Entry:** `server/index.js`; app `server/app.js`; port `5110`, service id `surf`; pages in
14 `server/views/index.js`, `server/render/layout.js`; API in `server/http/`; Watch/Ride/Web clients in
15 `server/watch/`, `server/ride/`, `server/web/`; auth in `server/auth/`.
16- **No database, no Valkey, no Events.** The only state is the 30 s Watch feed cache, the 60 s Ride
17 cache, the SDK's JWKS cache and the in-memory watch parties. **A restart ends every watch party.**
18- **Pages:** server-rendered through the OpenVibe Frame (`openvibe-shared/shell`), readable without
19 JavaScript; `public/js/surf.js` adds the TV (native HLS, else `/vendor/hls.light.min.js` only when
20 needed), the Web form and the home timer. Own CSS in `public/css`; no bundler.
21
22## Who calls it, what it calls
23
24Public reads (`/api/v1/watch/feed`, `/api/v1/ride/maps`, `/api/v1/ride/maps/:id`) need no token.
25Signed-in writes use the person's httpOnly `surf_at` Network cookie.
26
27- **OpenVibe.Live** — the Watch feed source (live streams, clips, moments, DVR playlist), with each
28 source given 2 s; a failed source is named in `sources` and makes the page `partial`, never fatal.
29- **OpenVibe.Games** — the Ride map catalogue and the embed origin
30 (`https://openvibe.games/surf/embed?map=<id>&embed=1`); until Games' surf routes exist, Games answers
31 404 and Surf serves an empty catalogue plus `games: "not_ready"`.
32- **OpenVibe.Actor** — `POST /api/v1/tasks` with the person's own Bearer token, an `Idempotency-Key` and
33 the standing brief; the task belongs to the person's allowance and task list. Actor's 4xx keeps its
34 status and words; no answer is `502 actor.unavailable`.
35- **OpenVibe.Network** — OAuth `surf` client (PKCE S256, state, server-side code exchange), JWKS and
36 sign-in.
37- **OpenVibe.Chat** — only a validated room link the host pasted; Surf has no chat.
38
39## Frontend loading model
40
41Every page is server-rendered with the shared Frame; `sf-` ids/classes are stable for scripts and tests.
42A page for a signed-in person is `private, no-store`; a public page is `public, max-age=60` with
43`Vary: Cookie`. Interactive extras live in `public/js/surf.js` (TV, form, timer) and are progressive
44enhancement only. The SSE route `GET /watch/p/<id>/events` needs `proxy_buffering off`, no gzip and a
451-hour read timeout in nginx.
46
47## Auth and security
48
49OAuth 2 authorization code + PKCE S256 with the `surf` Network client; the session lives in httpOnly
50cookies and no script can read or log it. The only place the token goes is the `Authorization` header
51of a request to the configured Actor origin. A Web task must come from this site's own `Origin` (a
52cross-site request could spend the visitor's allowance); `/web/go` also checks `Sec-Fetch-Site`; `next`
53after sign-in is this site's own path only and control characters/backslashes are refused.
54
55Watch-together: only the host may command, from this site's own Origin; a member's or another site's
56command is 403. Party ids are unguessable; at most 200 parties and 50 members each.
57
58## Deploy
59
60`sudo ovhost deploy surf`: checkout `/opt/openvibe.surf`, unit `openvibe-surf.service` on
61`127.0.0.1:5110`, env `/etc/openvibe/surf.env`. nginx
62`deploy/nginx/openvibe.surf.conf` (installed with `ov-vhost-install`): `/api` reads only,
63`/api/v1/web/` and `/web/go` the forms zone, `/auth` GET only, `/auth/me` the API budget,
64`/internal` and `/metrics` 404. Readiness answers `degraded` (HTTP 200) when Network, Live or Games is
65unreachable. Rollback with `sudo ovhost rollback surf --to <sha>`.
66
67## Commands and tests
68
69```bash
70npm ci
71npm run dev # http://127.0.0.1:5110
72npm test # every test/*.test.js against in-process stand-ins
73npm test -- watch # only matching files
74npm test -- --strict # a skipped file fails the run
75```
76
77`test/run.js` uses `openvibe-shared/test-runner` (60 s, serial); each file boots Surf against
78in-process Network/Live/Games/Actor stand-ins, never the internet. Tests cover the merged feed and
79partial sources, Ride fallbacks, the Actor task (Idempotency-Key, statuses), watch parties and SSE,
80the pages, sign-in origin rules, discovery/agent twins, contrast and links.
81
82## Conventions and pitfalls
83
84- CommonJS, 4-space indent, single quotes, semicolons; problem+json for API errors; escaped,
85 server-rendered HTML. No database and no schema: do not add a migration without a design change.
86- **Nothing durable may be assumed.** Parties, caches and tokens-in-flight die on restart; a feature
87 that needs to survive one belongs in another service, not in Surf's memory.
88- **Never weaken the origin checks** on `/api/v1/web/tasks`, `/web/go` or a party command; they protect
89 a person's Actor allowance and their party.
90- **A Live/Games/Actor failure is named, not hidden:** `sources`/`partial`, `games: "not_ready"` and
91 `502 actor.unavailable` are the contract; never fabricate feed entries or map data.
92- **The Web question never goes in a URL:** `/web/go` posts it and 303s to the Actor task page.
93- **Progressive enhancement:** every page must work without `public/js/surf.js`; the HLS vendor file is
94 loaded only when native HLS is missing.
95