openvibers/openvibe.play README.md

README.md

main 9.8 KB · 156 lines · markdown

1# OpenVibe.Play
2
3**Achievements, leaderboards and cloud saves for every OpenVibe game.** https://play.openvibe.network
4
5**Live site:** https://play.openvibe.network · **API docs:** https://openvibe.services · **For AI:** https://play.openvibe.network/llms.txt
6**Run it:** `npm ci && npm run dev` (Node 22 or newer) · **Tests:** `npm test`
7
8OpenVibe.Play is the network's game services (OpenVibe.Games [ADR-0007](https://github.com/OpenVibers/OpenVibe.Games/blob/main/docs/adr/0007-rewrite-the-moddable-base.md)
9decision 10). A game registers its **title** with its own OpenVibe token, posts what its players did, and anyone reads
10its boards. The rules of each game stay in the game; Play keeps the record and answers it.
11
12- **Titles** belong to the principal that registered them (a game's service or app token). Only that principal writes to
13 a title; to anyone else it does not exist.
14- **Your own game, from the sandbox** (ADR-014 opt-in): any developer makes a project on openvibe.services and a sandbox
15 app with Play's three capabilities (in Network's default sandbox allowance). A sandbox app registers up to 3 titles
16 whose ids start with `sandbox-`, with guest players only (nothing adopted, no person's record touched), never listed,
17 counted or indexed: readable at their id, marked `sandbox: true`, and deleted by their app (`DELETE /titles/:title`;
18 a production title is never deleted). A production app (staff turn production on for the
19 project) registers the real name; it never takes a `sandbox-` id.
20- **Achievements**, including hidden ones whose name shows only once unlocked. A definition left out of an update is
21 retired, never deleted: what players earned stays.
22- **Leaderboards** of three kinds. `best` ranks each player's best score. `sum` ranks the total of theirs. `time`
23 ranks each player's fastest run, in whole milliseconds (at most 24 hours), fastest first. Each reads this week (from
24 Monday 00:00 UTC) or all time, 25 a page, and a tie goes to whoever got there first. A board's `unit` says what its
25 scores mean: `"ms"` for `time`, `"points"` for the others.
26- **Players** are people (`usr_…`) or guests a game names (`guest:<opaque>`). When a guest signs in, the game adopts
27 them and their unlocks and scores become the person's.
28- **Cloud saves** (`migrations/0004_saves.sql`): a player's progress follows them to any device. Up to 8 slots per
29 player per title, each any JSON value of at most 64 KB, versioned: a write names the version it read and a stale one
30 is refused (409 `play.save_conflict`). The title's own token writes (`play.result.write`) and reads
31 (`play.player.read`); a person lists and deletes their own at `/me/saves` and sees them on /me. A guest's slots move
32 to the person on adoption (the newer save wins a clash); they are in the account export and erased with the account.
33- **Idempotent writes.** A round's scores carry a `ref`; posting it again changes nothing. An unlock answers only the
34 achievements that were new.
35- **Personal data.** Only people's rows are: they are in the person's account export and erased with their account
36 (ADR-033). A board shows a name and a score, never a player id.
37
38Every page is server-rendered and readable without JavaScript: the home (every game and this week's leaders), a title,
39a board, your achievements and the API docs.
40
41## Purpose
42
43One record of what players did in OpenVibe games, shared by every game and read anywhere. A game keeps its own rules and
44reports their results; Play stores the achievements unlocked and the scores posted, ranks them, and serves them to the
45game, to the player's account page and to any page that wants a board.
46
47## Owns
48
49- Titles, their achievement definitions and their boards (`play_titles`, `play_achievements`, `play_boards`).
50- Unlocks, scores and cloud saves per player (`play_unlocks`, `play_scores`, `play_saves`) and guest adoption.
51- The `/api/v1` read and write API. The player pages now live on OpenVibe.Games (achievements, leaderboards, developers); every old page at play.openvibe.network answers 301 to its counterpart there, and the API stays here.
52- Request telemetry (`telemetry_samples`, `0003_telemetry.sql`): each HTTP request aggregated by route template, method, status class,
53 never by raw URL; `/shared`, static assets, health, readiness and `/metrics` are not counted. `server/app.js` builds the collector,
54 `server/index.js` flushes per key one `http.request` sample plus the active-requests, p95 latency and event-loop-lag gauges each
55 minute and on stop (openvibe-sdk 0.46, `platform.telemetry-sample@1`), and rows older than 14 days are deleted daily.
56
57## Does not own
58
59- A game's rules, rounds or anti-cheat: the game decides what counts and posts the result. Games' local tables stay in
60 OpenVibe.Games, which posts to Play.
61- People's accounts, names shown elsewhere, or sign-in: OpenVibe.Network. Rewards worn across the network (items) are
62 OpenVibe.Inventory's; OpenCoins are the Network wallet's.
63
64## Depends on
65
66- **OpenVibe.Network:** service and app tokens (audience `openvibe.play`), the JWKS that verifies them, grants for the
67 `play.*` capabilities, and person sign-in for "your achievements".
68- **OpenVibe.Events:** the account subscriptions (export and deletion), created at boot when
69 `EVENTS_URL`, `PLAY_EVENTS_SECRET` and the client secret are set.
70- **PostgreSQL** (`ov_play`; embedded PGlite in development and tests), Valkey for per-caller limits when configured.
71
72## Capabilities
73
74| Capability | Lets a caller |
75|---|---|
76| `play.title.manage` | register or update its own title (name, url, summary, achievements, boards) |
77| `play.result.write` | post scores and unlocks to its own title, and adopt a guest as a person |
78| `play.player.read` | read a player's unlocks in its own title |
79
80A title belongs to the principal that registered it; another principal's write is refused as if the title did not
81exist. Reads of titles and boards are public.
82
83## Acceptance
84
85Tested in `test/` (`npm test`, every file in its own process on PGlite; `npm run test:pg` on PostgreSQL):
86`play-api.test.js` (titles, owner-only writes, idempotent scores, best and sum boards with the Monday week, guest
87adoption, secret achievements, retired definitions, a person's own achievements, the API), `moved-pages.test.js`
88(the page redirects and the crawl files), `time-boards.test.js`
89(time boards, their ties, validation and units), `time-migration.test.js` (the kind CHECK widening over existing
90boards), `account-data.test.js`
91(export and erasure), the auth, limits, redirects, open-redirect, nginx and security tests.
92
93## Security
94
95- Writes need a Network token with the capability, for audience `openvibe.play`; a principal writes only its own titles.
96- Every write is idempotent (`ref` for scores, set semantics for unlocks) and limited per calling principal
97 (`http/caller-limits.js`), sign-in routes per address.
98- Boards show a name and a score, never a player id; guests are opaque ids chosen by the game.
99- Report vulnerabilities as [SECURITY.md](SECURITY.md) says.
100
101## Deploy
102
103`sudo ovhost deploy play` on the host: unit `openvibe-play.service` on 127.0.0.1:5040, env `/etc/openvibe/play.env`,
104database `ov_play`, nginx vhost `deploy/nginx/play.openvibe.network.conf` on the openvibe.network wildcard certificate.
105Migrations run at boot.
106
107## API (`/api/v1`, problem+json errors)
108
109| Route | Who | What |
110|---|---|---|
111| `GET /titles`, `GET /titles/:title` | anyone (CORS) | the games, their achievements (with how many players have each) and boards |
112| `GET /titles/:title/boards/:board?period=week\|all&page=` | anyone (CORS) | a board: `rank`, `name`, `score`, `signed_in`, `unit` (`ms` for a time board, `points` otherwise) |
113| `GET /people/:subject/achievements` | anyone (CORS) | a person's unlocks in every game, `{ subject, total, titles }`: their profile on OpenVibe.Network and any site's showcase (only people, never a guest) |
114| `GET /me/achievements` | a person | their unlocks in every game |
115| `PUT /titles/:title` | `play.title.manage` | register or update a title: name, url, summary, achievements, boards |
116| `POST /titles/:title/scores` | `play.result.write` | `{ board, ref, entries: [{ player, name, score }] }`, up to 100; a `time` board's score is whole milliseconds from 0 to 86400000 |
117| `POST /titles/:title/unlocks` | `play.result.write` | `{ player, achievements: [id] }` → the new ones |
118| `POST /titles/:title/players/:guest/adopt` | `play.result.write` | `{ person: "usr_…" }`: a guest signed in |
119| `GET /titles/:title/players/:player/achievements` | `play.player.read` | a player's unlocks in your title |
120
121Tokens are Network service or app tokens for audience `openvibe.play`; the contracts are `play.*` in
122[openvibe-contracts](https://github.com/OpenVibers/OpenVibe.Contracts) (0.172.1).
123
124## Run
125
126```sh
127npm install
128npm run dev # http://localhost:5040, embedded PGlite in data/pglite
129npm test # every test on PGlite; npm run test:pg on PostgreSQL
130```
131
132Configuration is environment only (`.env.example`, names only). Production: unit `openvibe-play.service` on
133127.0.0.1:5040, database `ov_play`, nginx vhost `deploy/nginx/play.openvibe.network.conf` on the openvibe.network
134wildcard certificate, deployed with `sudo ovhost deploy play`.
135
136## Layout
137
138```
139server/play/store.js titles, achievements, boards, scores, unlocks, adoption
140server/play/account-data.js account export and deletion (ADR-033)
141server/http/api.js /api/v1
142server/http/moved.js the old page addresses, 301 to OpenVibe.Games
143server/http/discovery.js robots.txt and llms.txt (sitemap.xml and llms-full.txt redirect to Games)
144migrations/ the schema (0001 the store, 0002 the retired merges table)
145```
146
147## License
148
149AGPL-3.0. Part of the [OpenVibe network](https://openvibe.network).
150
151<!-- versions:start -->
152- openvibe-contracts: v0.419.0
153- openvibe-sdk: v0.161.0
154- openvibe-shared: v4.241.0
155<!-- versions:end -->
156