openvibers/openvibe.run STATUS.json
STATUS.json
| 1 | { |
| 2 | "repository": "OpenVibers/OpenVibe.Run", |
| 3 | "stage": "alpha", |
| 4 | "code": true, |
| 5 | "wave": "W20", |
| 6 | "plan": "OpenVibe Updated Plan Based On Codebase (plan T14, ADR-034)", |
| 7 | "runtime": "node 22, express 4, PostgreSQL 18 (openvibe-sdk/db) + Valkey 9", |
| 8 | "port": 4920, |
| 9 | "domain": "openvibe.run", |
| 10 | "contracts": "openvibe-contracts v0.422.0", |
| 11 | "sdk": "openvibe-sdk v0.161.0", |
| 12 | "shared": "openvibe-shared v4.237.0", |
| 13 | "packages": [ |
| 14 | "openvibe-contracts v0.422.0", |
| 15 | "openvibe-sdk v0.161.0", |
| 16 | "openvibe-shared v4.237.0" |
| 17 | ], |
| 18 | "capabilities": [ |
| 19 | "run.job.submit", |
| 20 | "run.job.read", |
| 21 | "run.job.list", |
| 22 | "run.job.cancel", |
| 23 | "run.job.stream", |
| 24 | "run.job.admin", |
| 25 | "run.session.create", |
| 26 | "run.session.read", |
| 27 | "run.session.control" |
| 28 | ], |
| 29 | "events": [ |
| 30 | "run.job.queued", |
| 31 | "run.job.started", |
| 32 | "run.job.succeeded", |
| 33 | "run.job.failed", |
| 34 | "run.job.cancelled", |
| 35 | "run.job.expired", |
| 36 | "run.session.started", |
| 37 | "run.session.ended" |
| 38 | ], |
| 39 | "deployed": true, |
| 40 | "deployment": "openvibe-ovh: unit openvibe-run.service on 127.0.0.1:4920 since 2026-10-07 (release layout /opt/openvibe.run, env /etc/openvibe/run.env, database ov_run), deployed with `sudo ovhost deploy run`. The job API is reached by first-party callers on loopback; jobs are placed on people's own Bot nodes through Bot's internal jobs API (bot.job.dispatch). openvibe.run itself is parked (a 302 to openvibe.network) until Run has a product surface; the vhost in deploy/nginx/openvibe.run.conf is installed then. Its rules stay: /api/health, /api/ready, /release.json and the job API proxied, the client address from $remote_addr only, /internal/ and /metrics refused at the edge.", |
| 41 | "features": [ |
| 42 | "the job API (/api/v1, one capability per route): POST /jobs (run.job.submit), GET /jobs/:id (run.job.read), GET /jobs/ (run.job.list), POST /jobs/:id/cancel (run.job.cancel), POST /jobs/:id/stream/ticket and GET /jobs/:id/stream (run.job.stream), GET /admin/jobs (run.job.admin)", |
| 43 | "the project and the requester come from the token (claims.project_id, claims.sub), never from the body; another project's job is 404 run.job_not_found, never 403", |
| 44 | "idempotent submit: the same Idempotency-Key (or body.idempotency_key) from the same project with the same body answers the first job (200, created false); with a different body it is 409 run.idempotency.conflict", |
| 45 | "every request body and query is validated against its released contract (run.job-create-request@1, run.job-list-query@1) and every answer against run.job-read-result@1, run.job-create-result@1, run.job-list-result@1, run.job-stream-ticket-result@1", |
| 46 | "limits bounded by the project's caps (RUN_LIMITS_*, 422 run.limits.exceeded) and args over 512 KiB refused 413 run.args_too_large, never silently lowered; requirements defaults to the contract's platform.workload-requirements@1 default (run.<class>, mobility job, latency_class background, objective balanced)", |
| 47 | "the run.job.queued|started|succeeded|failed|cancelled|expired events written in the same transaction as the state change, the outbox row keyed run:<job id>:<state> (a replay is one row) with a contract-valid evt_ document id on the published envelope, relayed from run_events_outbox by the openvibe-sdk outbox", |
| 48 | "the job stream: a two-minute single-use RS256 stream ticket (audience openvibe.run, typ run-stream, its own key — never Network's) opens text/event-stream of run.job-stream-event@1, replayed from the last 1 MiB with monotonic seq as the SSE id and Last-Event-ID resume", |
| 49 | "keyset paging on (created_at, id) with next_cursor null on the last page; run.job.admin lists any project's jobs (project_id in the query) and reads and cancels any job", |
| 50 | "the dispatcher bridge to OpenVibe.Bot (plan T14 step 6, server/dispatch/index.js + server/jobs/poller.js): a queued job is placed on a node from the Fabric offers (server/dispatch/placement.js, openvibe-sdk/placement plan(), the job's own platform.workload-requirements@1, no eligible offer → run.job.unplaceable) and handed to Bot once (server/dispatch/bot.js POST /api/v1/jobs, Network client-credentials token audience openvibe.bot holding bot.job.dispatch, cached until 60 s before expiry, never logged); every placed or running job is mirrored from Bot's own row (state, placement, timings, exit, result, metered wall clock) by a poller on RUN_POLL_MS (default 1000 ms, started by server/index.js, nothing at module load), each state change emitting its run.job.* event once in the same transaction and feeding the job's SSE stream with the node's stdout as output events; a job Bot answers 404 for after Run sent it fails run.job.worker_failed after RUN_DISPATCH_UNKNOWN_POLLS polls, and Bot can neither rename the node or the project of a job Run placed nor move its state backwards", |
| 51 | "cancel reaches the node: a queued job is cancelled at once, a placed or running one is marked cancel_requested and Bot is asked for job_cancel (once per job, retried by the poller) — the job settles cancelled when Bot reports it", |
| 52 | "ttl_ms expiry settles the job expired (run.job.ttl) and asks Bot to stop a job a node may hold", |
| 53 | "the job stream also opens with a Bearer service token holding run.job.stream for the job's project, besides the two-minute ticket", |
| 54 | "run_jobs (migrations/0001_jobs.sql) holds every job, its state, placement, timings, exit, result and the metered wall clock", |
| 55 | "Bot owns the node link and the per-second metering of the jobs it carries (plan T14 L1); Run holds a Billing token of its own (billing.usage.record, audience openvibe.billing) used only for the external managed-browser provider's jobs", |
| 56 | "plan T14 R4a built, not deployed: the managed-browser provider adapter (server/dispatch/browser-provider.js; RUN_BROWSER_PROVIDER_URL / _API_KEY / _TIMEOUT_MS, off while unset) places class browser jobs on kind=provider offers named browser-provider, validates every answer against browser-provider.job-create-result@1 / job-state@1 and never adopts a malformed one; Run meters those jobs itself, the provider's claimed wall time clamped to Run's observed duration + 5 s, and relays the readings from run_usage_outbox (migrations/0003_usage_outbox.sql) to Billing (RUN_BILLING_URL; unset, they wait and the jobs still run)", |
| 57 | "truthful readiness (GET /api/ready: db required; valkey, Network JWKS, Events, Bot reachability and the poller's own status reported, never claimed) and the loopback-only /metrics gauges (jobs queued, active, ended, outbox backlog, streams)", |
| 58 | "plan T11 step 4 built, not deployed: /llms.txt and /llms-full.txt (server/web/discovery.js, public pages and docs only, never the API, admin, a job or a stream) and IndexNow (INDEXNOW_KEY: serves GET /<key>.txt and pings the public pages once at boot; unset, the key route is 404 and nothing is sent)", |
| 59 | "a person's own machine (user-owned offer read from Network's internal list) runs only that person's jobs naming user-owned; a delegating service cancels and streams (by ticket) the jobs it started for a person", |
| 60 | "the Arena's race on the one engine (compare-arena-ecosystem.md §3, server/races/): POST /api/v1/races starts one child linux job per side under one parent race with the same share of the budget each, GET /api/v1/races/:id returns each side's output so far and its receipts, and POST /api/v1/races/:id/judgement records a bench.match@1 on Bench (source arena, shown order, blind true, human_pairwise with a keyed person hash) before the names are revealed; only the person who started it, through the same service, may read or judge", |
| 61 | "the race play as data (agents.play@1 1.1.0, server/plays/race.js): id race, one role per side, one parallel phase, an even budget split, blind true and stop when person, listed by GET /api/v1/plays; the matchmaker falls back to two entrants of different families from RUN_RACE_ENTRANTS while Bench has no matchmaker endpoint (Contracts 0.422.0 has no run.race@1 yet, so the race request/answer is validated in server/races/service.js)" |
| 62 | ], |
| 63 | "updated": "2026-11-13" |
| 64 | } |
| 65 |