openvibers/openvibe.run CHANGELOG.md
CHANGELOG.md
| 1 | # Changelog |
| 2 | |
| 3 | Notable changes to OpenVibe.Run. The service is unreleased; everything below is `0.1.0` in progress. |
| 4 | |
| 5 | ## Unreleased |
| 6 | |
| 7 | ### Added |
| 8 | |
| 9 | - The Arena's race on the one engine (compare-arena-ecosystem.md §3, contracts 0.422.0): the `race` play as |
| 10 | `agents.play@1` (one role per side, one parallel phase, an even budget split, `blind: true`, stop on person), listed |
| 11 | by `GET /api/v1/plays`; `POST /api/v1/races` starts one child `linux` job per side (2–4) under one parent race with |
| 12 | the same share of the budget each and answers the sides labelled `a, b, …` without names; `GET /api/v1/races/:id` |
| 13 | returns each side's output so far and its receipts when ended, the names only after a judgement; |
| 14 | `POST /api/v1/races/:id/judgement` records one `bench.match@1` on Bench (source `arena`, shown order, `blind: true`, |
| 15 | the receipts, a `human_pairwise` judgement with an HMAC of the person) before revealing the names. Only the person |
| 16 | who started the race, through the same first-party service, may read or judge it. `matchmaker: true` picks two |
| 17 | entrants of different model families from `RUN_RACE_ENTRANTS` while Bench has no matchmaker endpoint. Migration |
| 18 | 0020 adds `run_races`. The race request/answer has no released contract yet, so it is validated in |
| 19 | `server/races/service.js`; everything it stores or sends is validated where a contract exists. |
| 20 | - With `RUN_SESSION_ORIGINS_ENABLED=1`, a browser session may name exact HTTP(S) origins at creation. Run stores the |
| 21 | list with the session, places it on a user-owned desk that can enforce the boundary, and sends the same list when |
| 22 | leasing that desk. Commands cannot replace the list. The switch is off by default. |
| 23 | |
| 24 | - One Computer (plan 2026-11-07, contracts 0.327.0): `run.session@1` carries `task_id` (the OpenVibe.Actor task the |
| 25 | session is the Computer of, so the session points back at the task) and `curtain` (the privacy screen). |
| 26 | `GET /api/v1/sessions/{id}/still` answers the desk's newest frame as a small JPEG (at most 640 px wide, cached for a |
| 27 | second so it captures at most once a second) from the desk's own capture on the session's lease; it is the session |
| 28 | person's alone, a signed-out visitor gets 404 like a missing session, `Cache-Control` is `private, no-store`, and |
| 29 | while `POST /api/v1/sessions/{id}/curtain` holds it up the answer is the curtain picture, never the screen. |
| 30 | Migration 0008 adds `task_id` and `curtain`. |
| 31 | - Placement A1 (openvibe-sdk 0.76.0): `server/dispatch/placement.js` calls `place()` — Bench priors (an hour's |
| 32 | cache), the person's learned counts and one plain line — and the same answer is stored on the job. `GET |
| 33 | /api/v1/jobs/:id?explain=1` answers the line beside the contract body. `placement_outcomes` (migration 0007) learns |
| 34 | one outcome per ended job: exit 0 is ok, a failure or a timeout is not; a cancelled job is not counted. |
| 35 | - The repository skeleton (plan T14 step 4, R1a): `server/config.js` on port 4920, `server/db.js`, |
| 36 | `server/app.js`, `server/index.js`, `server/observability.js` (`/api/health`, `/api/ready`, `/release.json`, |
| 37 | loopback-only `/metrics`), `server/api/auth.js`, `server/events/outbox.js`, `migrations/0001_jobs.sql` |
| 38 | (`run_jobs`, `run_events_outbox`), `STATUS.json`, the nginx vhost and the systemd unit. |
| 39 | - The job API (plan T14 step 5, R1b): `POST /api/v1/jobs` (`run.job.submit`), `GET /api/v1/jobs/:id` |
| 40 | (`run.job.read`), `GET /api/v1/jobs` (`run.job.list`), `POST /api/v1/jobs/:id/cancel` (`run.job.cancel`), |
| 41 | `POST /api/v1/jobs/:id/stream/ticket` and `GET /api/v1/jobs/:id/stream` (`run.job.stream`) and |
| 42 | `GET /api/v1/admin/jobs` (`run.job.admin`) — each validated against its released contract, with the project and |
| 43 | the requester taken from the token alone. |
| 44 | - `run.job.queued|started|succeeded|failed|cancelled|expired` through `run_events_outbox`, written in the same |
| 45 | transaction as the state change, and the two-minute single-use stream ticket with the SSE stream (replay, |
| 46 | monotonic `seq`, `Last-Event-ID`). |
| 47 | - `server/dispatch/index.js`: the dispatcher-bridge seam (plan T14 step 6) as a no-op, so every job stays `queued`. |
| 48 | |
| 49 | - The dispatcher bridge to OpenVibe.Bot (plan T14 step 6, R1c) and placement (step 7, R3): `server/dispatch/bot.js` |
| 50 | (the client — `send`/`cancel`/`state` with Run's Network client-credentials token for audience `openvibe.bot` |
| 51 | holding `bot.job.dispatch`, cached until 60 s before expiry), `server/dispatch/placement.js` (the Fabric offers |
| 52 | plus `openvibe-sdk/placement` `plan()`, with `user-owned` eligible only when the job's requirements name it) and |
| 53 | `server/jobs/poller.js` (`RUN_POLL_MS`, default 1000 ms): the ttl sweep, the send, the mirror of Bot's state with |
| 54 | one `run.job.*` event per state change, the node's stdout as stream `output` events, and the job_cancel. |
| 55 | - `GET /api/v1/jobs/:id/stream` also opens with a Bearer service token holding `run.job.stream` for the job's |
| 56 | project, beside the ticket. |
| 57 | - The managed-browser provider adapter (plan T14 R4a, `server/dispatch/browser-provider.js`): class `browser` jobs |
| 58 | placed on a `kind=provider` offer named `browser-provider` are sent to the provider's API |
| 59 | (`browser-provider.job-create-request@1`, the frame alone: no project, subject or requester), mirrored, cancelled |
| 60 | and expired there; every answer is validated against `job-create-result@1` / `job-state@1` and a malformed one is |
| 61 | never adopted. Configured by `RUN_BROWSER_PROVIDER_URL`, `RUN_BROWSER_PROVIDER_API_KEY` (Run's env only, never |
| 62 | logged) and `RUN_BROWSER_PROVIDER_TIMEOUT_MS` (8000); off while the URL or the key is unset. |
| 63 | - Run's own metering of those jobs: the provider's claimed wall time is clamped to Run's observed duration + 5 s |
| 64 | (a larger claim is logged and never billed), queued as `platform.usage-sample@1` readings in `run_usage_outbox` |
| 65 | (`migrations/0003_usage_outbox.sql`) with the end state, and relayed to Billing (`RUN_BILLING_URL`) with a Run |
| 66 | token for audience `openvibe.billing` holding `billing.usage.record` — used for nothing else. |
| 67 | |
| 68 | ### Changed |
| 69 | |
| 70 | - Cancelling a `placed` job no longer settles it at once: a job Run has handed to Bot is marked `cancel_requested`, |
| 71 | Bot is asked for `job_cancel` and the job ends when Bot reports it — Run never says `cancelled` for work a node may |
| 72 | still be doing. A `queued` job (sent nowhere) is still cancelled at once. |
| 73 | - `POST /api/v1/jobs` no longer places eagerly: the poller sends each queued job, so a submit never waits on Network |
| 74 | or Bot and a job whose lifetime ran out is never sent. |
| 75 | - `RUN_JOBS_INTERVAL_MS`/`RUN_DISPATCH_INTERVAL_MS` are replaced by `RUN_POLL_MS` (the poller's own interval). |
| 76 | - `usage.wall_ms`/`usage.seconds` are Bot's metered wall clock: 0 until the job's `job_exit` (Bot is the meter). |
| 77 | |
| 78 | ### Notes |
| 79 | |
| 80 | - OpenVibe.Bot owns the node link and the per-second metering of the jobs it carries (plan T14 L1); Run's Billing |
| 81 | token is for the external provider's jobs only. |
| 82 | - The repo is not deployed; the first deploy needs `ovhost`'s inventory, the `ov_run` database, |
| 83 | `/etc/openvibe/run.env` and `openvibe.run` DNS (see STATUS.json). |
| 84 |