openvibers/openvibe.contracts Commits
Add sandbox-provider job contracts and planned dispatch capability
Changes
@@@ -4,6 +4,16 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) thfrom `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,the notes were in the tag and commit messages (`git tag -n1`).++## 0.159.0 — 2026-10-14++++- **Contracts 0.159.0: sandbox-provider job contracts (plan T14 R4b, ADR-034; planned, internal).** What OpenVibe.Run++ sends to and reads from an external sandbox provider such as E2B or Fly Machines, the same shape as the++ browser-provider set: `sandbox-provider.job-create-request@1` (`id`, `class` code or linux, `args`, `inputs` pinned by++ sha256, `limits`, `egress`; no project, subject, requester or credential), `sandbox-provider.job-state@1` (`state`,++ `started_ms`, `finished_ms`, `wall_ms` as the provider's claim only, `exit_reason`, `exit_code`, `result`, `stdout` in++ Bot's ring shape) and `sandbox-provider.job-create-result@1` (`{ job, created }`). New capability++ `sandbox_provider.job.dispatch` (Run's own outbound call, `planned`, `implementedBy` empty). Additive only.++## 0.158.0 — 2026-10-14- **`live.chat_mirror.write` is retired.** The Live read mirror is gone: Chat stopped its capture triggers@@@ -6255,6 +6255,36 @@"status": "planned","adr": "ADR-034"},++ {++ "id": "sandbox-provider.job-create-request",++ "version": "1.0.0",++ "owner": "run",++ "schema": "sandbox-provider/job-create-request.v1.json",++ "visibility": "internal",++ "compatibility": "backward",++ "status": "planned",++ "adr": "ADR-034"++ },++ {++ "id": "sandbox-provider.job-create-result",++ "version": "1.0.0",++ "owner": "run",++ "schema": "sandbox-provider/job-create-result.v1.json",++ "visibility": "internal",++ "compatibility": "backward",++ "status": "planned",++ "adr": "ADR-034"++ },++ {++ "id": "sandbox-provider.job-state",++ "version": "1.0.0",++ "owner": "run",++ "schema": "sandbox-provider/job-state.v1.json",++ "visibility": "internal",++ "compatibility": "backward",++ "status": "planned",++ "adr": "ADR-034"++ },{"id": "run.job-create-request","version": "1.0.0",nnew file mode 100644@@@ -0,0 +1,74 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/sandbox-provider/job-create-request.v1.json",++ "title": "SandboxProviderJobCreateRequest",++ "description": "sandbox-provider.job-create-request@1 (PLANNED; plan T14 R4b, ADR-034): the body OpenVibe.Run POSTs to an external sandbox provider's (such as E2B or Fly Machines) job endpoint (capability sandbox_provider.job.dispatch). ID is the platform.job@1 id (job_<ULID>) and is also the idempotency key: a repeat POST with the same id answers the first job (200, created false) and never starts a second. CLASS is code or linux, the platform.runtime-class@1 classes a sandbox hosts. ARGS is the job's declared input as JSON (Run refuses more than 512 KiB serialized before it ever sends). INPUTS are files, each an OpenVibe.Media object pinned by sha256; never a URL, so the provider fetches nothing a caller chose. LIMITS are the hard caps of platform.job@1 (wall_ms required; cpu_ms and mem_bytes when the provider can enforce them). EGRESS is the network the job may reach, the values of OpenVibe.Node's worker.egress, as platform.job@1 `net` carries it. The request carries no project, subject, requester, token or other credential: the provider never learns who pays, and Run authenticates to it with a key from Run's own environment, sent in a header and never in this body. The provider's answer is sandbox-provider.job-create-result@1.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "class",++ "args",++ "inputs",++ "limits",++ "egress"++ ],++ "properties": {++ "id": {++ "$ref": "../platform/job-frame.v1.json#/$defs/job_id"++ },++ "class": {++ "enum": ["code", "linux"]++ },++ "args": {++ "type": "object",++ "description": "The job's declared input as JSON; Run enforces the 512 KiB cap before sending."++ },++ "inputs": {++ "type": "array",++ "maxItems": 32,++ "items": {++ "$ref": "../run/job-create-request.v1.json#/$defs/input"++ },++ "description": "Files for the job, each an OpenVibe.Media object pinned by sha256; names are unique; empty when the job has none."++ },++ "limits": {++ "type": "object",++ "additionalProperties": false,++ "description": "Caps on the running job (platform.job@1 limits); the provider ends a job that hits one with exit_reason limit.",++ "required": [++ "wall_ms"++ ],++ "properties": {++ "wall_ms": {++ "type": "integer",++ "minimum": 1,++ "description": "Running time from the job's start"++ },++ "cpu_ms": {++ "type": "integer",++ "minimum": 1,++ "description": "CPU time, user plus system; when the provider can enforce it"++ },++ "mem_bytes": {++ "type": "integer",++ "minimum": 1,++ "description": "Peak resident memory; when the provider can enforce it"++ }++ }++ },++ "egress": {++ "$ref": "../run/job-create-request.v1.json#/$defs/egress"++ }++ },++ "examples": [++ {++ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "class": "code",++ "args": { "entry": "main.py" },++ "inputs": [],++ "limits": { "wall_ms": 60000 },++ "egress": "public"++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,37 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/sandbox-provider/job-create-result.v1.json",++ "title": "SandboxProviderJobCreateResult",++ "description": "sandbox-provider.job-create-result@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) answer to OpenVibe.Run's job POST (sandbox-provider.job-create-request@1): 201 { job, created: true } when it accepted a new job, 200 { job, created: false } for an idempotent repeat of an id it already holds (the first job's state, nothing started twice). JOB is sandbox-provider.job-state@1 and Run validates it before adopting any of it. A refused POST answers a problem instead (4xx; 5xx is retried by Run on its next tick), never this contract. For the later calls, GET <provider>/jobs/:id answers sandbox-provider.job-state@1 and POST <provider>/jobs/:id/cancel answers it too; a 404 on either means the provider does not know the job.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "job",++ "created"++ ],++ "properties": {++ "job": {++ "$ref": "job-state.v1.json"++ },++ "created": {++ "type": "boolean",++ "description": "True when this POST created the job (201); false for an idempotent repeat (200)."++ }++ },++ "examples": [++ {++ "job": {++ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "state": "queued",++ "started_ms": null,++ "finished_ms": null,++ "wall_ms": 0,++ "exit_reason": null,++ "exit_code": null,++ "result": null,++ "stdout": null++ },++ "created": true++ }++ ]++}nnew file mode 100644@@@ -0,0 +1,143 @@++{++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/sandbox-provider/job-state.v1.json",++ "title": "SandboxProviderJobState",++ "description": "sandbox-provider.job-state@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) state of one job, the body of GET <provider>/jobs/:id and the `job` of sandbox-provider.job-create-result@1. OpenVibe.Run validates it before adopting any of it: a malformed state is never adopted, and the job stays as Run last saw it. A 404 on GET or on cancel means the provider does not know the job (Run fails the job run.job.worker_failed). STATE is queued, running, then one end state: succeeded, failed, cancelled or expired. WALL_MS is the provider's CLAIM of the time the job ran, nothing more: Run bills from its own observed duration and clamps a larger claim (observed plus 5 s), so this field never sets a price. STARTED_MS and FINISHED_MS are the provider's clock, Unix ms, null until they happen. EXIT_REASON uses the reasons of platform.job-frame@1 `job_exit` (exited, cancelled, ttl, limit, stopped, failed) and is null until the job ended. EXIT_CODE is the job's exit status, null when it was killed or never ran. RESULT is the declared output (an object, at most 256 KiB serialized), null unless the job succeeded. STDOUT is the captured output in the ring shape OpenVibe.Bot returns (bot.job-dispatch-result@1) so Run's poller streams it the same way, or null when there is none. There is no node, project or class field: a provider cannot name a node or a project.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "state",++ "started_ms",++ "finished_ms",++ "wall_ms",++ "exit_reason",++ "exit_code",++ "result",++ "stdout"++ ],++ "properties": {++ "id": {++ "$ref": "../platform/job-frame.v1.json#/$defs/job_id"++ },++ "state": {++ "type": "string",++ "enum": [++ "queued",++ "running",++ "succeeded",++ "failed",++ "cancelled",++ "expired"++ ],++ "description": "queued (accepted, not started), running, then one end state: succeeded (exited 0), failed (another exit code, a limit, or the provider could not run it), cancelled (a cancel), expired (the job's lifetime ran out)."++ },++ "started_ms": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "When the job started, provider's clock, Unix ms; null until it started."++ },++ "finished_ms": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "When the job ended, provider's clock, Unix ms; null until it ended."++ },++ "wall_ms": {++ "type": "integer",++ "minimum": 0,++ "description": "The provider's CLAIM of the running time; Run clamps it to its own observed duration plus 5 s and never bills above that. 0 while the job has not run."++ },++ "exit_reason": {++ "type": [++ "string",++ "null"++ ],++ "enum": [++ "exited",++ "cancelled",++ "ttl",++ "limit",++ "stopped",++ "failed",++ null++ ],++ "description": "Why the job ended, as platform.job-frame@1 job_exit reason; null until it ended."++ },++ "exit_code": {++ "type": [++ "integer",++ "null"++ ],++ "description": "The script's exit status; null when it was killed or never ran."++ },++ "result": {++ "type": [++ "object",++ "null"++ ],++ "description": "The declared output as a JSON object, at most 256 KiB serialized; null unless the job succeeded."++ },++ "stdout": {++ "anyOf": [++ {++ "type": "object",++ "additionalProperties": false,++ "description": "The held tail of the job's stdout, the ring shape of bot.job-dispatch-result@1.",++ "required": [++ "text",++ "first_seq",++ "last_seq",++ "truncated"++ ],++ "properties": {++ "text": {++ "type": "string",++ "maxLength": 1048576,++ "description": "The held chunks joined in order, at most 1 MiB."++ },++ "first_seq": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The chunk_seq of the first held chunk; null when none is held."++ },++ "last_seq": {++ "type": "integer",++ "minimum": 0,++ "description": "The highest chunk_seq seen for the job; 0 when none."++ },++ "truncated": {++ "type": "boolean",++ "description": "True when an earlier chunk fell off the ring."++ }++ }++ },++ {++ "type": "null"++ }++ ],++ "description": "The captured stdout, or null when the job has none."++ }++ },++ "examples": [++ {++ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "state": "succeeded",++ "started_ms": 1790935200250,++ "finished_ms": 1790935202600,++ "wall_ms": 2350,++ "exit_reason": "exited",++ "exit_code": 0,++ "result": { "title": "Example Domain" },++ "stdout": { "text": "loaded\n", "first_seq": 1, "last_seq": 1, "truncated": false }++ }++ ]++}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"run_1","class":"code","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"none"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"browser","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"none"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[{"name":"page.html","url":"https://example.com/page.html"}],"limits":{"wall_ms":60000},"egress":"none"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[],"limits":{"cpu_ms":1000},"egress":"none"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"none","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"none","subject":"user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"deny"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{"entry":"main.py"},"inputs":[{"name":"script.js","media_id":"med_01JAB2C3D4E5F6G7H8J9K0MNPQ","sha256":"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08","size_bytes":482}],"limits":{"wall_ms":120000,"cpu_ms":120000,"mem_bytes":1073741824},"egress":"public"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"none"}nnew file mode 100644@@@ -0,0 +1 @@++{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null},"created":"yes"}nnew file mode 100644@@@ -0,0 +1 @@++{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null},"created":true,"sent":true}nnew file mode 100644@@@ -0,0 +1 @@++{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null,"project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"},"created":true}nnew file mode 100644@@@ -0,0 +1 @@++{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null}}nnew file mode 100644@@@ -0,0 +1 @@++{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"placed","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null},"created":true}nnew file mode 100644@@@ -0,0 +1 @@++{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null},"created":true}nnew file mode 100644@@@ -0,0 +1 @@++{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":2350,"exit_reason":"exited","exit_code":0,"result":{"title":"Example Domain"},"stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}},"created":false}nnew file mode 100644@@@ -0,0 +1 @@++{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"running","started_ms":1790935200250,"finished_ms":null,"wall_ms":1200,"exit_reason":null,"exit_code":null,"result":null,"stdout":{"text":"loaded\n","first_seq":1,"last_seq":1,"truncated":false}},"created":false}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null,"class":"code"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"exit_reason":null,"exit_code":null,"result":null,"stdout":null}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"running","started_ms":-5,"finished_ms":null,"wall_ms":1200,"exit_reason":null,"exit_code":null,"result":null,"stdout":{"text":"loaded\n","first_seq":1,"last_seq":1,"truncated":false}}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":-1,"exit_reason":"exited","exit_code":0,"result":{"title":"Example Domain"},"stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null,"node_id":"dev_01JAB2C3D4E5F6G7H8J9K0MNPQ"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null,"project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":2350,"exit_reason":"exited","exit_code":0,"result":"ok","stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"running","started_ms":1,"finished_ms":null,"wall_ms":1,"exit_reason":null,"exit_code":null,"result":null,"stdout":{"text":"x"}}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":2350,"exit_reason":"crashed","exit_code":0,"result":{"title":"Example Domain"},"stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"placed","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":"2350","exit_reason":"exited","exit_code":0,"result":{"title":"Example Domain"},"stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"failed","started_ms":1790935200250,"finished_ms":1790935260250,"wall_ms":60000,"exit_reason":"limit","exit_code":null,"result":null,"stdout":null}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"running","started_ms":1790935200250,"finished_ms":null,"wall_ms":1200,"exit_reason":null,"exit_code":null,"result":null,"stdout":{"text":"loaded\n","first_seq":1,"last_seq":1,"truncated":false}}nnew file mode 100644@@@ -0,0 +1 @@++{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":2350,"exit_reason":"exited","exit_code":0,"result":{"title":"Example Domain"},"stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}}@@@ -72199,6 +72199,289 @@]}},++ "sandbox-provider.job-create-request": {++ "version": "1.0.0",++ "owner": "run",++ "status": "planned",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/sandbox-provider/job-create-request.v1.json",++ "title": "SandboxProviderJobCreateRequest",++ "description": "sandbox-provider.job-create-request@1 (PLANNED; plan T14 R4b, ADR-034): the body OpenVibe.Run POSTs to an external sandbox provider's (such as E2B or Fly Machines) job endpoint (capability sandbox_provider.job.dispatch). ID is the platform.job@1 id (job_<ULID>) and is also the idempotency key: a repeat POST with the same id answers the first job (200, created false) and never starts a second. CLASS is code or linux, the platform.runtime-class@1 classes a sandbox hosts. ARGS is the job's declared input as JSON (Run refuses more than 512 KiB serialized before it ever sends). INPUTS are files, each an OpenVibe.Media object pinned by sha256; never a URL, so the provider fetches nothing a caller chose. LIMITS are the hard caps of platform.job@1 (wall_ms required; cpu_ms and mem_bytes when the provider can enforce them). EGRESS is the network the job may reach, the values of OpenVibe.Node's worker.egress, as platform.job@1 `net` carries it. The request carries no project, subject, requester, token or other credential: the provider never learns who pays, and Run authenticates to it with a key from Run's own environment, sent in a header and never in this body. The provider's answer is sandbox-provider.job-create-result@1.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "class",++ "args",++ "inputs",++ "limits",++ "egress"++ ],++ "properties": {++ "id": {++ "$ref": "../platform/job-frame.v1.json#/$defs/job_id"++ },++ "class": {++ "enum": [++ "code",++ "linux"++ ]++ },++ "args": {++ "type": "object",++ "description": "The job's declared input as JSON; Run enforces the 512 KiB cap before sending."++ },++ "inputs": {++ "type": "array",++ "maxItems": 32,++ "items": {++ "$ref": "../run/job-create-request.v1.json#/$defs/input"++ },++ "description": "Files for the job, each an OpenVibe.Media object pinned by sha256; names are unique; empty when the job has none."++ },++ "limits": {++ "type": "object",++ "additionalProperties": false,++ "description": "Caps on the running job (platform.job@1 limits); the provider ends a job that hits one with exit_reason limit.",++ "required": [++ "wall_ms"++ ],++ "properties": {++ "wall_ms": {++ "type": "integer",++ "minimum": 1,++ "description": "Running time from the job's start"++ },++ "cpu_ms": {++ "type": "integer",++ "minimum": 1,++ "description": "CPU time, user plus system; when the provider can enforce it"++ },++ "mem_bytes": {++ "type": "integer",++ "minimum": 1,++ "description": "Peak resident memory; when the provider can enforce it"++ }++ }++ },++ "egress": {++ "$ref": "../run/job-create-request.v1.json#/$defs/egress"++ }++ },++ "examples": [++ {++ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "class": "code",++ "args": {++ "entry": "main.py"++ },++ "inputs": [],++ "limits": {++ "wall_ms": 60000++ },++ "egress": "public"++ }++ ]++ }++ },++ "sandbox-provider.job-create-result": {++ "version": "1.0.0",++ "owner": "run",++ "status": "planned",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/sandbox-provider/job-create-result.v1.json",++ "title": "SandboxProviderJobCreateResult",++ "description": "sandbox-provider.job-create-result@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) answer to OpenVibe.Run's job POST (sandbox-provider.job-create-request@1): 201 { job, created: true } when it accepted a new job, 200 { job, created: false } for an idempotent repeat of an id it already holds (the first job's state, nothing started twice). JOB is sandbox-provider.job-state@1 and Run validates it before adopting any of it. A refused POST answers a problem instead (4xx; 5xx is retried by Run on its next tick), never this contract. For the later calls, GET <provider>/jobs/:id answers sandbox-provider.job-state@1 and POST <provider>/jobs/:id/cancel answers it too; a 404 on either means the provider does not know the job.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "job",++ "created"++ ],++ "properties": {++ "job": {++ "$ref": "job-state.v1.json"++ },++ "created": {++ "type": "boolean",++ "description": "True when this POST created the job (201); false for an idempotent repeat (200)."++ }++ },++ "examples": [++ {++ "job": {++ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "state": "queued",++ "started_ms": null,++ "finished_ms": null,++ "wall_ms": 0,++ "exit_reason": null,++ "exit_code": null,++ "result": null,++ "stdout": null++ },++ "created": true++ }++ ]++ }++ },++ "sandbox-provider.job-state": {++ "version": "1.0.0",++ "owner": "run",++ "status": "planned",++ "schema": {++ "$schema": "https://json-schema.org/draft/2020-12/schema",++ "$id": "https://openvibe.network/contracts/sandbox-provider/job-state.v1.json",++ "title": "SandboxProviderJobState",++ "description": "sandbox-provider.job-state@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) state of one job, the body of GET <provider>/jobs/:id and the `job` of sandbox-provider.job-create-result@1. OpenVibe.Run validates it before adopting any of it: a malformed state is never adopted, and the job stays as Run last saw it. A 404 on GET or on cancel means the provider does not know the job (Run fails the job run.job.worker_failed). STATE is queued, running, then one end state: succeeded, failed, cancelled or expired. WALL_MS is the provider's CLAIM of the time the job ran, nothing more: Run bills from its own observed duration and clamps a larger claim (observed plus 5 s), so this field never sets a price. STARTED_MS and FINISHED_MS are the provider's clock, Unix ms, null until they happen. EXIT_REASON uses the reasons of platform.job-frame@1 `job_exit` (exited, cancelled, ttl, limit, stopped, failed) and is null until the job ended. EXIT_CODE is the job's exit status, null when it was killed or never ran. RESULT is the declared output (an object, at most 256 KiB serialized), null unless the job succeeded. STDOUT is the captured output in the ring shape OpenVibe.Bot returns (bot.job-dispatch-result@1) so Run's poller streams it the same way, or null when there is none. There is no node, project or class field: a provider cannot name a node or a project.",++ "type": "object",++ "additionalProperties": false,++ "required": [++ "id",++ "state",++ "started_ms",++ "finished_ms",++ "wall_ms",++ "exit_reason",++ "exit_code",++ "result",++ "stdout"++ ],++ "properties": {++ "id": {++ "$ref": "../platform/job-frame.v1.json#/$defs/job_id"++ },++ "state": {++ "type": "string",++ "enum": [++ "queued",++ "running",++ "succeeded",++ "failed",++ "cancelled",++ "expired"++ ],++ "description": "queued (accepted, not started), running, then one end state: succeeded (exited 0), failed (another exit code, a limit, or the provider could not run it), cancelled (a cancel), expired (the job's lifetime ran out)."++ },++ "started_ms": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "When the job started, provider's clock, Unix ms; null until it started."++ },++ "finished_ms": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 0,++ "description": "When the job ended, provider's clock, Unix ms; null until it ended."++ },++ "wall_ms": {++ "type": "integer",++ "minimum": 0,++ "description": "The provider's CLAIM of the running time; Run clamps it to its own observed duration plus 5 s and never bills above that. 0 while the job has not run."++ },++ "exit_reason": {++ "type": [++ "string",++ "null"++ ],++ "enum": [++ "exited",++ "cancelled",++ "ttl",++ "limit",++ "stopped",++ "failed",++ null++ ],++ "description": "Why the job ended, as platform.job-frame@1 job_exit reason; null until it ended."++ },++ "exit_code": {++ "type": [++ "integer",++ "null"++ ],++ "description": "The script's exit status; null when it was killed or never ran."++ },++ "result": {++ "type": [++ "object",++ "null"++ ],++ "description": "The declared output as a JSON object, at most 256 KiB serialized; null unless the job succeeded."++ },++ "stdout": {++ "anyOf": [++ {++ "type": "object",++ "additionalProperties": false,++ "description": "The held tail of the job's stdout, the ring shape of bot.job-dispatch-result@1.",++ "required": [++ "text",++ "first_seq",++ "last_seq",++ "truncated"++ ],++ "properties": {++ "text": {++ "type": "string",++ "maxLength": 1048576,++ "description": "The held chunks joined in order, at most 1 MiB."++ },++ "first_seq": {++ "type": [++ "integer",++ "null"++ ],++ "minimum": 1,++ "description": "The chunk_seq of the first held chunk; null when none is held."++ },++ "last_seq": {++ "type": "integer",++ "minimum": 0,++ "description": "The highest chunk_seq seen for the job; 0 when none."++ },++ "truncated": {++ "type": "boolean",++ "description": "True when an earlier chunk fell off the ring."++ }++ }++ },++ {++ "type": "null"++ }++ ],++ "description": "The captured stdout, or null when the job has none."++ }++ },++ "examples": [++ {++ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",++ "state": "succeeded",++ "started_ms": 1790935200250,++ "finished_ms": 1790935202600,++ "wall_ms": 2350,++ "exit_reason": "exited",++ "exit_code": 0,++ "result": {++ "title": "Example Domain"++ },++ "stdout": {++ "text": "loaded\n",++ "first_seq": 1,++ "last_seq": 1,++ "truncated": false++ }++ }++ ]++ }++ },"run.job-create-request": {"version": "1.0.0","owner": "run",@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Actor API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.AI API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Billing API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Blog API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Bot API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Chat API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Community API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Coupons API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Deals API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Events API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Food API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Games API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Host API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Inventory API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Live API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.MediaHub API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Media API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Network API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.News API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenRestream API",-- "version": "0.158.0",-- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Reviews API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Run API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Search API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Services API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Sources API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Space API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tips API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Tools API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Trade API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.VIP API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Watch API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Wiki API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -3,8 +3,8 @@"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema","info": {"title": "OpenVibe.Work API",-- "version": "0.158.0",-- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",++ "version": "0.159.0",++ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.","license": {"name": "MIT","identifier": "MIT"@@@ -170008,6 +170008,132 @@ export interface BrowserProviderJobState {} | null;}++/** [email protected] (owner: run) */++/**++ * sandbox-provider.job-create-request@1 (PLANNED; plan T14 R4b, ADR-034): the body OpenVibe.Run POSTs to an external sandbox provider's (such as E2B or Fly Machines) job endpoint (capability sandbox_provider.job.dispatch). ID is the platform.job@1 id (job_<ULID>) and is also the idempotency key: a repeat POST with the same id answers the first job (200, created false) and never starts a second. CLASS is code or linux, the platform.runtime-class@1 classes a sandbox hosts. ARGS is the job's declared input as JSON (Run refuses more than 512 KiB serialized before it ever sends). INPUTS are files, each an OpenVibe.Media object pinned by sha256; never a URL, so the provider fetches nothing a caller chose. LIMITS are the hard caps of platform.job@1 (wall_ms required; cpu_ms and mem_bytes when the provider can enforce them). EGRESS is the network the job may reach, the values of OpenVibe.Node's worker.egress, as platform.job@1 `net` carries it. The request carries no project, subject, requester, token or other credential: the provider never learns who pays, and Run authenticates to it with a key from Run's own environment, sent in a header and never in this body. The provider's answer is sandbox-provider.job-create-result@1.++ */++export interface SandboxProviderJobCreateRequest {++ /**++ * platform.job@1 `id`++ */++ id: string;++ class: "code" | "linux";++ /**++ * The job's declared input as JSON; Run enforces the 512 KiB cap before sending.++ */++ args: {};++ /**++ * Files for the job, each an OpenVibe.Media object pinned by sha256; names are unique; empty when the job has none.++ *++ * @maxItems 32++ */++ inputs: {++ /**++ * File name in the job directory; no path separators.++ */++ name: string;++ media_id: string;++ /**++ * Lowercase hex SHA-256 of the object's bytes.++ */++ sha256: string;++ size_bytes?: number;++ }[];++ /**++ * Caps on the running job (platform.job@1 limits); the provider ends a job that hits one with exit_reason limit.++ */++ limits: {++ /**++ * Running time from the job's start++ */++ wall_ms: number;++ /**++ * CPU time, user plus system; when the provider can enforce it++ */++ cpu_ms?: number;++ /**++ * Peak resident memory; when the provider can enforce it++ */++ mem_bytes?: number;++ };++ /**++ * OpenVibe.Node worker.egress values: none (no network), public (the internet without private, link-local or the network's own ranges), openvibe-only (the network's own endpoints only).++ */++ egress: "none" | "public" | "openvibe-only";++}++++/** [email protected] (owner: run) */++/**++ * sandbox-provider.job-create-result@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) answer to OpenVibe.Run's job POST (sandbox-provider.job-create-request@1): 201 { job, created: true } when it accepted a new job, 200 { job, created: false } for an idempotent repeat of an id it already holds (the first job's state, nothing started twice). JOB is sandbox-provider.job-state@1 and Run validates it before adopting any of it. A refused POST answers a problem instead (4xx; 5xx is retried by Run on its next tick), never this contract. For the later calls, GET <provider>/jobs/:id answers sandbox-provider.job-state@1 and POST <provider>/jobs/:id/cancel answers it too; a 404 on either means the provider does not know the job.++ */++export interface SandboxProviderJobCreateResult {++ job: SandboxProviderJobState;++ /**++ * True when this POST created the job (201); false for an idempotent repeat (200).++ */++ created: boolean;++}++++/** [email protected] (owner: run) */++/**++ * sandbox-provider.job-state@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) state of one job, the body of GET <provider>/jobs/:id and the `job` of sandbox-provider.job-create-result@1. OpenVibe.Run validates it before adopting any of it: a malformed state is never adopted, and the job stays as Run last saw it. A 404 on GET or on cancel means the provider does not know the job (Run fails the job run.job.worker_failed). STATE is queued, running, then one end state: succeeded, failed, cancelled or expired. WALL_MS is the provider's CLAIM of the time the job ran, nothing more: Run bills from its own observed duration and clamps a larger claim (observed plus 5 s), so this field never sets a price. STARTED_MS and FINISHED_MS are the provider's clock, Unix ms, null until they happen. EXIT_REASON uses the reasons of platform.job-frame@1 `job_exit` (exited, cancelled, ttl, limit, stopped, failed) and is null until the job ended. EXIT_CODE is the job's exit status, null when it was killed or never ran. RESULT is the declared output (an object, at most 256 KiB serialized), null unless the job succeeded. STDOUT is the captured output in the ring shape OpenVibe.Bot returns (bot.job-dispatch-result@1) so Run's poller streams it the same way, or null when there is none. There is no node, project or class field: a provider cannot name a node or a project.++ */++export interface SandboxProviderJobState {++ /**++ * platform.job@1 `id`++ */++ id: string;++ /**++ * queued (accepted, not started), running, then one end state: succeeded (exited 0), failed (another exit code, a limit, or the provider could not run it), cancelled (a cancel), expired (the job's lifetime ran out).++ */++ state: "queued" | "running" | "succeeded" | "failed" | "cancelled" | "expired";++ /**++ * When the job started, provider's clock, Unix ms; null until it started.++ */++ started_ms: number | null;++ /**++ * When the job ended, provider's clock, Unix ms; null until it ended.++ */++ finished_ms: number | null;++ /**++ * The provider's CLAIM of the running time; Run clamps it to its own observed duration plus 5 s and never bills above that. 0 while the job has not run.++ */++ wall_ms: number;++ /**++ * Why the job ended, as platform.job-frame@1 job_exit reason; null until it ended.++ */++ exit_reason: "exited" | "cancelled" | "ttl" | "limit" | "stopped" | "failed" | null;++ /**++ * The script's exit status; null when it was killed or never ran.++ */++ exit_code: number | null;++ /**++ * The declared output as a JSON object, at most 256 KiB serialized; null unless the job succeeded.++ */++ result: {} | null;++ /**++ * The captured stdout, or null when the job has none.++ */++ stdout: {++ /**++ * The held chunks joined in order, at most 1 MiB.++ */++ text: string;++ /**++ * The chunk_seq of the first held chunk; null when none is held.++ */++ first_seq: number | null;++ /**++ * The highest chunk_seq seen for the job; 0 when none.++ */++ last_seq: number;++ /**++ * True when an earlier chunk fell off the ring.++ */++ truncated: boolean;++ } | null;++}++/** [email protected] (owner: run) *//*** run.job-create-request@1 (PLANNED; plan T14 R1, ADR-034): the body of POST /api/v1/jobs on OpenVibe.Run (capability run.job.submit). The job belongs to the project the caller's token names, or, for a first-party service acting for a person (X-OV-Subject), the project it names with X-OV-Project once Network confirms the person is an admin or owner of it (ADR-003 amendment 2026-10-12); the person is then the requester and the service is recorded as via. CLASS is a platform.runtime-class@1 name; Run accepts any of them but places a job only on a node that advertises worker:<class> (platform.resource-offer@1), so a class no node runs today (browser, linux, desktop, gpu) ends expired with run.job.unplaceable. ARTIFACT is the pre-registered artifact to run (name and one exact version), required for function and code, as platform.job@1 carries it. ARGS is the artifact's declared input as JSON (at most 512 KiB serialized, else 413). INPUTS are files placed in the job directory before the process starts, each an OpenVibe.Media object pinned by its sha256: a size or digest mismatch fails the job (run.job.input_digest_mismatch) before the process ever runs; Run never fetches a caller-chosen URL. LIMITS are hard caps merged stricter with the node's own (platform.job@1 limits); a request over the project's tier cap is refused 422 run.limits.exceeded, never silently lowered. EGRESS is the network the job may reach, the values of OpenVibe.Node's worker.egress, carried per job as the same-named platform.job@1 net: none (default; no network), public or openvibe-only. A job asking for public or openvibe-only is placed only on a node whose worker.egress allows it; a worker whose host policy is stricter runs the stricter one. REQUIREMENTS (platform.workload-requirements@1) narrow placement; absent means { kind: run.<class>, mobility: job, latency_class: background, objective: balanced }. IDEMPOTENCY_KEY: a repeat from the same project within 24 h with an identical body answers the first job (200, created false); with a different body 409 run.idempotency.conflict.nnew file mode 100644@@@ -0,0 +1,18 @@++{++ "id": "sandbox_provider.job.dispatch",++ "version": "1.0",++ "owner": "run",++ "status": "planned",++ "visibility": "internal",++ "sensitive": true,++ "description": "PLANNED (plan T14 R4b, ADR-034): OpenVibe.Run's outbound call to the external sandbox provider (such as E2B or Fly Machines) it places class code and linux jobs on: POST <provider>/jobs {id, class, args, inputs, limits, egress} (idempotent by job id; 201 created, 200 repeat), GET <provider>/jobs/:id for the state, POST <provider>/jobs/:id/cancel to stop it; a 404 on the last two means the provider does not know the job. The request carries no project, subject or credential: the provider never learns who pays. Run authenticates with a key from its own environment, never from an offer and never logged, validates every answer against sandbox-provider.job-state@1 (a malformed state is never adopted), and bills from its own observed duration, clamping the provider's claimed wall_ms to that plus 5 s. Run's own outbound call; no token grants it and it is never delegated.",++ "permissions": [],++ "resourceConstraints": [++ "project"++ ],++ "quotaClass": "bot-jobs",++ "events": [],++ "implementedBy": [],++ "inputSchema": "sandbox-provider.job-create-request@1",++ "outputSchema": "sandbox-provider.job-create-result@1"++}@@@ -1,12 +1,12 @@{"name": "openvibe-contracts",-- "version": "0.124.0",++ "version": "0.159.0","lockfileVersion": 3,"requires": true,"packages": {"": {"name": "openvibe-contracts",-- "version": "0.124.0",++ "version": "0.159.0","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -1,6 +1,6 @@{"name": "openvibe-contracts",-- "version": "0.158.0",++ "version": "0.159.0","description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.","main": "index.js","types": "generated/typescript/index.d.ts",@@@ -122,4 +122,16 @@ const dispatch = contracts.capabilities.get('browser_provider.job.dispatch');ok(dispatch && dispatch.owner === 'run' && dispatch.status === 'planned' && dispatch.visibility === 'internal' && dispatch.sensitive === true && dispatch.implementedBy.length === 0 && dispatch.permissions.length === 0, 'browser_provider.job.dispatch is a planned internal sensitive capability of Run');ok(dispatch.inputSchema === 'browser-provider.job-create-request@1' && dispatch.outputSchema === 'browser-provider.job-create-result@1' && !run.capabilities.includes('browser_provider.job.dispatch'), 'it names the two contracts and is not yet a capability Run serves');++// Run's outbound call to an external sandbox provider (T14 R4b): the browser-provider shape for classes code and linux.++const sp = id => contracts.schema(`sandbox-provider.${id}`);++for (const id of ['job-create-request', 'job-state', 'job-create-result']) {++ ok(sp(id).additionalProperties === false, `sandbox-provider.${id} refuses unknown fields`);++ if (id !== 'job-create-result') assert.deepStrictEqual(Object.keys(sp(id).properties).filter(k => k !== 'class'), Object.keys(bp(id).properties).filter(k => k !== 'class'));++}++checks += 2;++assert.deepStrictEqual(sp('job-create-request').properties.class.enum, ['code', 'linux']);++checks++;++const sandboxDispatch = contracts.capabilities.get('sandbox_provider.job.dispatch');++ok(sandboxDispatch && sandboxDispatch.owner === 'run' && sandboxDispatch.status === 'planned' && sandboxDispatch.visibility === 'internal' && sandboxDispatch.sensitive === true && sandboxDispatch.implementedBy.length === 0 && sandboxDispatch.inputSchema === 'sandbox-provider.job-create-request@1' && sandboxDispatch.outputSchema === 'sandbox-provider.job-create-result@1' && !run.capabilities.includes('sandbox_provider.job.dispatch'), 'sandbox_provider.job.dispatch is a planned internal sensitive capability of Run naming the two contracts');++console.log(`platform run: ${checks} job, frame and metering checks passed`);
@@ -4,6 +4,16 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
@@ -4,6 +4,16 @@ All notable changes to `openvibe-contracts`. Releases are git tags (`vX.Y.Z`) th
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
from `https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/<tag>`. Before v0.30.0,
the notes were in the tag and commit messages (`git tag -n1`).
the notes were in the tag and commit messages (`git tag -n1`).
+## 0.159.0 — 2026-10-14
+
+- **Contracts 0.159.0: sandbox-provider job contracts (plan T14 R4b, ADR-034; planned, internal).** What OpenVibe.Run
+ sends to and reads from an external sandbox provider such as E2B or Fly Machines, the same shape as the
+ browser-provider set: `sandbox-provider.job-create-request@1` (`id`, `class` code or linux, `args`, `inputs` pinned by
+ sha256, `limits`, `egress`; no project, subject, requester or credential), `sandbox-provider.job-state@1` (`state`,
+ `started_ms`, `finished_ms`, `wall_ms` as the provider's claim only, `exit_reason`, `exit_code`, `result`, `stdout` in
+ Bot's ring shape) and `sandbox-provider.job-create-result@1` (`{ job, created }`). New capability
+ `sandbox_provider.job.dispatch` (Run's own outbound call, `planned`, `implementedBy` empty). Additive only.
+
## 0.158.0 — 2026-10-14
## 0.158.0 — 2026-10-14
- **`live.chat_mirror.write` is retired.** The Live read mirror is gone: Chat stopped its capture triggers
- **`live.chat_mirror.write` is retired.** The Live read mirror is gone: Chat stopped its capture triggers
@@ -6255,6 +6255,36 @@
@@ -6255,6 +6255,36 @@
"status": "planned",
"status": "planned",
"adr": "ADR-034"
"adr": "ADR-034"
},
},
+ {
+ "id": "sandbox-provider.job-create-request",
+ "version": "1.0.0",
+ "owner": "run",
+ "schema": "sandbox-provider/job-create-request.v1.json",
+ "visibility": "internal",
+ "compatibility": "backward",
+ "status": "planned",
+ "adr": "ADR-034"
+ },
+ {
+ "id": "sandbox-provider.job-create-result",
+ "version": "1.0.0",
+ "owner": "run",
+ "schema": "sandbox-provider/job-create-result.v1.json",
+ "visibility": "internal",
+ "compatibility": "backward",
+ "status": "planned",
+ "adr": "ADR-034"
+ },
+ {
+ "id": "sandbox-provider.job-state",
+ "version": "1.0.0",
+ "owner": "run",
+ "schema": "sandbox-provider/job-state.v1.json",
+ "visibility": "internal",
+ "compatibility": "backward",
+ "status": "planned",
+ "adr": "ADR-034"
+ },
{
{
"id": "run.job-create-request",
"id": "run.job-create-request",
"version": "1.0.0",
"version": "1.0.0",
new file mode 100644
new file mode 100644
@@ -0,0 +1,74 @@
@@ -0,0 +1,74 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/sandbox-provider/job-create-request.v1.json",
+ "title": "SandboxProviderJobCreateRequest",
+ "description": "sandbox-provider.job-create-request@1 (PLANNED; plan T14 R4b, ADR-034): the body OpenVibe.Run POSTs to an external sandbox provider's (such as E2B or Fly Machines) job endpoint (capability sandbox_provider.job.dispatch). ID is the platform.job@1 id (job_<ULID>) and is also the idempotency key: a repeat POST with the same id answers the first job (200, created false) and never starts a second. CLASS is code or linux, the platform.runtime-class@1 classes a sandbox hosts. ARGS is the job's declared input as JSON (Run refuses more than 512 KiB serialized before it ever sends). INPUTS are files, each an OpenVibe.Media object pinned by sha256; never a URL, so the provider fetches nothing a caller chose. LIMITS are the hard caps of platform.job@1 (wall_ms required; cpu_ms and mem_bytes when the provider can enforce them). EGRESS is the network the job may reach, the values of OpenVibe.Node's worker.egress, as platform.job@1 `net` carries it. The request carries no project, subject, requester, token or other credential: the provider never learns who pays, and Run authenticates to it with a key from Run's own environment, sent in a header and never in this body. The provider's answer is sandbox-provider.job-create-result@1.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "class",
+ "args",
+ "inputs",
+ "limits",
+ "egress"
+ ],
+ "properties": {
+ "id": {
+ "$ref": "../platform/job-frame.v1.json#/$defs/job_id"
+ },
+ "class": {
+ "enum": ["code", "linux"]
+ },
+ "args": {
+ "type": "object",
+ "description": "The job's declared input as JSON; Run enforces the 512 KiB cap before sending."
+ },
+ "inputs": {
+ "type": "array",
+ "maxItems": 32,
+ "items": {
+ "$ref": "../run/job-create-request.v1.json#/$defs/input"
+ },
+ "description": "Files for the job, each an OpenVibe.Media object pinned by sha256; names are unique; empty when the job has none."
+ },
+ "limits": {
+ "type": "object",
+ "additionalProperties": false,
+ "description": "Caps on the running job (platform.job@1 limits); the provider ends a job that hits one with exit_reason limit.",
+ "required": [
+ "wall_ms"
+ ],
+ "properties": {
+ "wall_ms": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Running time from the job's start"
+ },
+ "cpu_ms": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "CPU time, user plus system; when the provider can enforce it"
+ },
+ "mem_bytes": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Peak resident memory; when the provider can enforce it"
+ }
+ }
+ },
+ "egress": {
+ "$ref": "../run/job-create-request.v1.json#/$defs/egress"
+ }
+ },
+ "examples": [
+ {
+ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "class": "code",
+ "args": { "entry": "main.py" },
+ "inputs": [],
+ "limits": { "wall_ms": 60000 },
+ "egress": "public"
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,37 @@
@@ -0,0 +1,37 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/sandbox-provider/job-create-result.v1.json",
+ "title": "SandboxProviderJobCreateResult",
+ "description": "sandbox-provider.job-create-result@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) answer to OpenVibe.Run's job POST (sandbox-provider.job-create-request@1): 201 { job, created: true } when it accepted a new job, 200 { job, created: false } for an idempotent repeat of an id it already holds (the first job's state, nothing started twice). JOB is sandbox-provider.job-state@1 and Run validates it before adopting any of it. A refused POST answers a problem instead (4xx; 5xx is retried by Run on its next tick), never this contract. For the later calls, GET <provider>/jobs/:id answers sandbox-provider.job-state@1 and POST <provider>/jobs/:id/cancel answers it too; a 404 on either means the provider does not know the job.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "job",
+ "created"
+ ],
+ "properties": {
+ "job": {
+ "$ref": "job-state.v1.json"
+ },
+ "created": {
+ "type": "boolean",
+ "description": "True when this POST created the job (201); false for an idempotent repeat (200)."
+ }
+ },
+ "examples": [
+ {
+ "job": {
+ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "state": "queued",
+ "started_ms": null,
+ "finished_ms": null,
+ "wall_ms": 0,
+ "exit_reason": null,
+ "exit_code": null,
+ "result": null,
+ "stdout": null
+ },
+ "created": true
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1,143 @@
@@ -0,0 +1,143 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/sandbox-provider/job-state.v1.json",
+ "title": "SandboxProviderJobState",
+ "description": "sandbox-provider.job-state@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) state of one job, the body of GET <provider>/jobs/:id and the `job` of sandbox-provider.job-create-result@1. OpenVibe.Run validates it before adopting any of it: a malformed state is never adopted, and the job stays as Run last saw it. A 404 on GET or on cancel means the provider does not know the job (Run fails the job run.job.worker_failed). STATE is queued, running, then one end state: succeeded, failed, cancelled or expired. WALL_MS is the provider's CLAIM of the time the job ran, nothing more: Run bills from its own observed duration and clamps a larger claim (observed plus 5 s), so this field never sets a price. STARTED_MS and FINISHED_MS are the provider's clock, Unix ms, null until they happen. EXIT_REASON uses the reasons of platform.job-frame@1 `job_exit` (exited, cancelled, ttl, limit, stopped, failed) and is null until the job ended. EXIT_CODE is the job's exit status, null when it was killed or never ran. RESULT is the declared output (an object, at most 256 KiB serialized), null unless the job succeeded. STDOUT is the captured output in the ring shape OpenVibe.Bot returns (bot.job-dispatch-result@1) so Run's poller streams it the same way, or null when there is none. There is no node, project or class field: a provider cannot name a node or a project.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "state",
+ "started_ms",
+ "finished_ms",
+ "wall_ms",
+ "exit_reason",
+ "exit_code",
+ "result",
+ "stdout"
+ ],
+ "properties": {
+ "id": {
+ "$ref": "../platform/job-frame.v1.json#/$defs/job_id"
+ },
+ "state": {
+ "type": "string",
+ "enum": [
+ "queued",
+ "running",
+ "succeeded",
+ "failed",
+ "cancelled",
+ "expired"
+ ],
+ "description": "queued (accepted, not started), running, then one end state: succeeded (exited 0), failed (another exit code, a limit, or the provider could not run it), cancelled (a cancel), expired (the job's lifetime ran out)."
+ },
+ "started_ms": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "When the job started, provider's clock, Unix ms; null until it started."
+ },
+ "finished_ms": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "When the job ended, provider's clock, Unix ms; null until it ended."
+ },
+ "wall_ms": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The provider's CLAIM of the running time; Run clamps it to its own observed duration plus 5 s and never bills above that. 0 while the job has not run."
+ },
+ "exit_reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "exited",
+ "cancelled",
+ "ttl",
+ "limit",
+ "stopped",
+ "failed",
+ null
+ ],
+ "description": "Why the job ended, as platform.job-frame@1 job_exit reason; null until it ended."
+ },
+ "exit_code": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "description": "The script's exit status; null when it was killed or never ran."
+ },
+ "result": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The declared output as a JSON object, at most 256 KiB serialized; null unless the job succeeded."
+ },
+ "stdout": {
+ "anyOf": [
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "description": "The held tail of the job's stdout, the ring shape of bot.job-dispatch-result@1.",
+ "required": [
+ "text",
+ "first_seq",
+ "last_seq",
+ "truncated"
+ ],
+ "properties": {
+ "text": {
+ "type": "string",
+ "maxLength": 1048576,
+ "description": "The held chunks joined in order, at most 1 MiB."
+ },
+ "first_seq": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The chunk_seq of the first held chunk; null when none is held."
+ },
+ "last_seq": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The highest chunk_seq seen for the job; 0 when none."
+ },
+ "truncated": {
+ "type": "boolean",
+ "description": "True when an earlier chunk fell off the ring."
+ }
+ }
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The captured stdout, or null when the job has none."
+ }
+ },
+ "examples": [
+ {
+ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "state": "succeeded",
+ "started_ms": 1790935200250,
+ "finished_ms": 1790935202600,
+ "wall_ms": 2350,
+ "exit_reason": "exited",
+ "exit_code": 0,
+ "result": { "title": "Example Domain" },
+ "stdout": { "text": "loaded\n", "first_seq": 1, "last_seq": 1, "truncated": false }
+ }
+ ]
+}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"run_1","class":"code","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"none"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"browser","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"none"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[{"name":"page.html","url":"https://example.com/page.html"}],"limits":{"wall_ms":60000},"egress":"none"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[],"limits":{"cpu_ms":1000},"egress":"none"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"none","project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"none","subject":"user:usr_01JAB2C3D4E5F6G7H8J9K0MNPQ"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"deny"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{"entry":"main.py"},"inputs":[{"name":"script.js","media_id":"med_01JAB2C3D4E5F6G7H8J9K0MNPQ","sha256":"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08","size_bytes":482}],"limits":{"wall_ms":120000,"cpu_ms":120000,"mem_bytes":1073741824},"egress":"public"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","class":"code","args":{},"inputs":[],"limits":{"wall_ms":60000},"egress":"none"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null},"created":"yes"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null},"created":true,"sent":true}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null,"project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"},"created":true}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null}}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"placed","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null},"created":true}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null},"created":true}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":2350,"exit_reason":"exited","exit_code":0,"result":{"title":"Example Domain"},"stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}},"created":false}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"job":{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"running","started_ms":1790935200250,"finished_ms":null,"wall_ms":1200,"exit_reason":null,"exit_code":null,"result":null,"stdout":{"text":"loaded\n","first_seq":1,"last_seq":1,"truncated":false}},"created":false}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null,"class":"code"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"exit_reason":null,"exit_code":null,"result":null,"stdout":null}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"running","started_ms":-5,"finished_ms":null,"wall_ms":1200,"exit_reason":null,"exit_code":null,"result":null,"stdout":{"text":"loaded\n","first_seq":1,"last_seq":1,"truncated":false}}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":-1,"exit_reason":"exited","exit_code":0,"result":{"title":"Example Domain"},"stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null,"node_id":"dev_01JAB2C3D4E5F6G7H8J9K0MNPQ"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null,"project_id":"prj_01JAB2C3D4E5F6G7H8J9K0MNPQ"}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":2350,"exit_reason":"exited","exit_code":0,"result":"ok","stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"running","started_ms":1,"finished_ms":null,"wall_ms":1,"exit_reason":null,"exit_code":null,"result":null,"stdout":{"text":"x"}}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":2350,"exit_reason":"crashed","exit_code":0,"result":{"title":"Example Domain"},"stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"placed","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":"2350","exit_reason":"exited","exit_code":0,"result":{"title":"Example Domain"},"stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"failed","started_ms":1790935200250,"finished_ms":1790935260250,"wall_ms":60000,"exit_reason":"limit","exit_code":null,"result":null,"stdout":null}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"queued","started_ms":null,"finished_ms":null,"wall_ms":0,"exit_reason":null,"exit_code":null,"result":null,"stdout":null}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"running","started_ms":1790935200250,"finished_ms":null,"wall_ms":1200,"exit_reason":null,"exit_code":null,"result":null,"stdout":{"text":"loaded\n","first_seq":1,"last_seq":1,"truncated":false}}
new file mode 100644
new file mode 100644
@@ -0,0 +1 @@
@@ -0,0 +1 @@
+{"id":"job_01JAB2C3D4E5F6G7H8J9K0MNPQ","state":"succeeded","started_ms":1790935200250,"finished_ms":1790935202600,"wall_ms":2350,"exit_reason":"exited","exit_code":0,"result":{"title":"Example Domain"},"stdout":{"text":"loaded\ndone\n","first_seq":1,"last_seq":2,"truncated":false}}
@@ -72199,6 +72199,289 @@
@@ -72199,6 +72199,289 @@
]
]
}
}
},
},
+ "sandbox-provider.job-create-request": {
+ "version": "1.0.0",
+ "owner": "run",
+ "status": "planned",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/sandbox-provider/job-create-request.v1.json",
+ "title": "SandboxProviderJobCreateRequest",
+ "description": "sandbox-provider.job-create-request@1 (PLANNED; plan T14 R4b, ADR-034): the body OpenVibe.Run POSTs to an external sandbox provider's (such as E2B or Fly Machines) job endpoint (capability sandbox_provider.job.dispatch). ID is the platform.job@1 id (job_<ULID>) and is also the idempotency key: a repeat POST with the same id answers the first job (200, created false) and never starts a second. CLASS is code or linux, the platform.runtime-class@1 classes a sandbox hosts. ARGS is the job's declared input as JSON (Run refuses more than 512 KiB serialized before it ever sends). INPUTS are files, each an OpenVibe.Media object pinned by sha256; never a URL, so the provider fetches nothing a caller chose. LIMITS are the hard caps of platform.job@1 (wall_ms required; cpu_ms and mem_bytes when the provider can enforce them). EGRESS is the network the job may reach, the values of OpenVibe.Node's worker.egress, as platform.job@1 `net` carries it. The request carries no project, subject, requester, token or other credential: the provider never learns who pays, and Run authenticates to it with a key from Run's own environment, sent in a header and never in this body. The provider's answer is sandbox-provider.job-create-result@1.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "class",
+ "args",
+ "inputs",
+ "limits",
+ "egress"
+ ],
+ "properties": {
+ "id": {
+ "$ref": "../platform/job-frame.v1.json#/$defs/job_id"
+ },
+ "class": {
+ "enum": [
+ "code",
+ "linux"
+ ]
+ },
+ "args": {
+ "type": "object",
+ "description": "The job's declared input as JSON; Run enforces the 512 KiB cap before sending."
+ },
+ "inputs": {
+ "type": "array",
+ "maxItems": 32,
+ "items": {
+ "$ref": "../run/job-create-request.v1.json#/$defs/input"
+ },
+ "description": "Files for the job, each an OpenVibe.Media object pinned by sha256; names are unique; empty when the job has none."
+ },
+ "limits": {
+ "type": "object",
+ "additionalProperties": false,
+ "description": "Caps on the running job (platform.job@1 limits); the provider ends a job that hits one with exit_reason limit.",
+ "required": [
+ "wall_ms"
+ ],
+ "properties": {
+ "wall_ms": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Running time from the job's start"
+ },
+ "cpu_ms": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "CPU time, user plus system; when the provider can enforce it"
+ },
+ "mem_bytes": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Peak resident memory; when the provider can enforce it"
+ }
+ }
+ },
+ "egress": {
+ "$ref": "../run/job-create-request.v1.json#/$defs/egress"
+ }
+ },
+ "examples": [
+ {
+ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "class": "code",
+ "args": {
+ "entry": "main.py"
+ },
+ "inputs": [],
+ "limits": {
+ "wall_ms": 60000
+ },
+ "egress": "public"
+ }
+ ]
+ }
+ },
+ "sandbox-provider.job-create-result": {
+ "version": "1.0.0",
+ "owner": "run",
+ "status": "planned",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/sandbox-provider/job-create-result.v1.json",
+ "title": "SandboxProviderJobCreateResult",
+ "description": "sandbox-provider.job-create-result@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) answer to OpenVibe.Run's job POST (sandbox-provider.job-create-request@1): 201 { job, created: true } when it accepted a new job, 200 { job, created: false } for an idempotent repeat of an id it already holds (the first job's state, nothing started twice). JOB is sandbox-provider.job-state@1 and Run validates it before adopting any of it. A refused POST answers a problem instead (4xx; 5xx is retried by Run on its next tick), never this contract. For the later calls, GET <provider>/jobs/:id answers sandbox-provider.job-state@1 and POST <provider>/jobs/:id/cancel answers it too; a 404 on either means the provider does not know the job.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "job",
+ "created"
+ ],
+ "properties": {
+ "job": {
+ "$ref": "job-state.v1.json"
+ },
+ "created": {
+ "type": "boolean",
+ "description": "True when this POST created the job (201); false for an idempotent repeat (200)."
+ }
+ },
+ "examples": [
+ {
+ "job": {
+ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "state": "queued",
+ "started_ms": null,
+ "finished_ms": null,
+ "wall_ms": 0,
+ "exit_reason": null,
+ "exit_code": null,
+ "result": null,
+ "stdout": null
+ },
+ "created": true
+ }
+ ]
+ }
+ },
+ "sandbox-provider.job-state": {
+ "version": "1.0.0",
+ "owner": "run",
+ "status": "planned",
+ "schema": {
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "$id": "https://openvibe.network/contracts/sandbox-provider/job-state.v1.json",
+ "title": "SandboxProviderJobState",
+ "description": "sandbox-provider.job-state@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) state of one job, the body of GET <provider>/jobs/:id and the `job` of sandbox-provider.job-create-result@1. OpenVibe.Run validates it before adopting any of it: a malformed state is never adopted, and the job stays as Run last saw it. A 404 on GET or on cancel means the provider does not know the job (Run fails the job run.job.worker_failed). STATE is queued, running, then one end state: succeeded, failed, cancelled or expired. WALL_MS is the provider's CLAIM of the time the job ran, nothing more: Run bills from its own observed duration and clamps a larger claim (observed plus 5 s), so this field never sets a price. STARTED_MS and FINISHED_MS are the provider's clock, Unix ms, null until they happen. EXIT_REASON uses the reasons of platform.job-frame@1 `job_exit` (exited, cancelled, ttl, limit, stopped, failed) and is null until the job ended. EXIT_CODE is the job's exit status, null when it was killed or never ran. RESULT is the declared output (an object, at most 256 KiB serialized), null unless the job succeeded. STDOUT is the captured output in the ring shape OpenVibe.Bot returns (bot.job-dispatch-result@1) so Run's poller streams it the same way, or null when there is none. There is no node, project or class field: a provider cannot name a node or a project.",
+ "type": "object",
+ "additionalProperties": false,
+ "required": [
+ "id",
+ "state",
+ "started_ms",
+ "finished_ms",
+ "wall_ms",
+ "exit_reason",
+ "exit_code",
+ "result",
+ "stdout"
+ ],
+ "properties": {
+ "id": {
+ "$ref": "../platform/job-frame.v1.json#/$defs/job_id"
+ },
+ "state": {
+ "type": "string",
+ "enum": [
+ "queued",
+ "running",
+ "succeeded",
+ "failed",
+ "cancelled",
+ "expired"
+ ],
+ "description": "queued (accepted, not started), running, then one end state: succeeded (exited 0), failed (another exit code, a limit, or the provider could not run it), cancelled (a cancel), expired (the job's lifetime ran out)."
+ },
+ "started_ms": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "When the job started, provider's clock, Unix ms; null until it started."
+ },
+ "finished_ms": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 0,
+ "description": "When the job ended, provider's clock, Unix ms; null until it ended."
+ },
+ "wall_ms": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The provider's CLAIM of the running time; Run clamps it to its own observed duration plus 5 s and never bills above that. 0 while the job has not run."
+ },
+ "exit_reason": {
+ "type": [
+ "string",
+ "null"
+ ],
+ "enum": [
+ "exited",
+ "cancelled",
+ "ttl",
+ "limit",
+ "stopped",
+ "failed",
+ null
+ ],
+ "description": "Why the job ended, as platform.job-frame@1 job_exit reason; null until it ended."
+ },
+ "exit_code": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "description": "The script's exit status; null when it was killed or never ran."
+ },
+ "result": {
+ "type": [
+ "object",
+ "null"
+ ],
+ "description": "The declared output as a JSON object, at most 256 KiB serialized; null unless the job succeeded."
+ },
+ "stdout": {
+ "anyOf": [
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "description": "The held tail of the job's stdout, the ring shape of bot.job-dispatch-result@1.",
+ "required": [
+ "text",
+ "first_seq",
+ "last_seq",
+ "truncated"
+ ],
+ "properties": {
+ "text": {
+ "type": "string",
+ "maxLength": 1048576,
+ "description": "The held chunks joined in order, at most 1 MiB."
+ },
+ "first_seq": {
+ "type": [
+ "integer",
+ "null"
+ ],
+ "minimum": 1,
+ "description": "The chunk_seq of the first held chunk; null when none is held."
+ },
+ "last_seq": {
+ "type": "integer",
+ "minimum": 0,
+ "description": "The highest chunk_seq seen for the job; 0 when none."
+ },
+ "truncated": {
+ "type": "boolean",
+ "description": "True when an earlier chunk fell off the ring."
+ }
+ }
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "The captured stdout, or null when the job has none."
+ }
+ },
+ "examples": [
+ {
+ "id": "job_01JAB2C3D4E5F6G7H8J9K0MNPQ",
+ "state": "succeeded",
+ "started_ms": 1790935200250,
+ "finished_ms": 1790935202600,
+ "wall_ms": 2350,
+ "exit_reason": "exited",
+ "exit_code": 0,
+ "result": {
+ "title": "Example Domain"
+ },
+ "stdout": {
+ "text": "loaded\n",
+ "first_seq": 1,
+ "last_seq": 1,
+ "truncated": false
+ }
+ }
+ ]
+ }
+ },
"run.job-create-request": {
"run.job-create-request": {
"version": "1.0.0",
"version": "1.0.0",
"owner": "run",
"owner": "run",
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Actor API",
"title": "OpenVibe.Actor API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Actor as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.AI API",
"title": "OpenVibe.AI API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.AI as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Billing API",
"title": "OpenVibe.Billing API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Billing as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Blog API",
"title": "OpenVibe.Blog API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Blog as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Bot API",
"title": "OpenVibe.Bot API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Bot as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Chat API",
"title": "OpenVibe.Chat API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Chat as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Community API",
"title": "OpenVibe.Community API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Community as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Coupons API",
"title": "OpenVibe.Coupons API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Coupons as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Deals API",
"title": "OpenVibe.Deals API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Deals as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Events API",
"title": "OpenVibe.Events API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Events as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Food API",
"title": "OpenVibe.Food API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Food as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Games API",
"title": "OpenVibe.Games API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Games as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Host API",
"title": "OpenVibe.Host API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Host as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Inventory API",
"title": "OpenVibe.Inventory API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Inventory as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Live API",
"title": "OpenVibe.Live API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Live as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.MediaHub API",
"title": "OpenVibe.MediaHub API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.MediaHub as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Media API",
"title": "OpenVibe.Media API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Media as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Network API",
"title": "OpenVibe.Network API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Network as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.News API",
"title": "OpenVibe.News API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.News as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenRestream API",
"title": "OpenRestream API",
- "version": "0.158.0",
- "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenRestream as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Reviews API",
"title": "OpenVibe.Reviews API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Reviews as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Run API",
"title": "OpenVibe.Run API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Run as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Search API",
"title": "OpenVibe.Search API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Search as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Services API",
"title": "OpenVibe.Services API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Services as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Sources API",
"title": "OpenVibe.Sources API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Sources as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Space API",
"title": "OpenVibe.Space API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Space as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tips API",
"title": "OpenVibe.Tips API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Tips as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Tools API",
"title": "OpenVibe.Tools API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Tools as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Trade API",
"title": "OpenVibe.Trade API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Trade as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.VIP API",
"title": "OpenVibe.VIP API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.VIP as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Watch API",
"title": "OpenVibe.Watch API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Watch as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Wiki API",
"title": "OpenVibe.Wiki API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Wiki as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -3,8 +3,8 @@
@@ -3,8 +3,8 @@
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
"info": {
"info": {
"title": "OpenVibe.Work API",
"title": "OpenVibe.Work API",
- "version": "0.158.0",
- "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.158.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
+ "version": "0.159.0",
+ "description": "Routes of OpenVibe.Work as the OpenVibe contracts describe them (openvibe-contracts 0.159.0): each operation lists the capabilities it performs; a token for the service's audience must carry one of them unless the route is open. Generated by openvibe-contracts lib/openapi.js; do not edit.",
"license": {
"license": {
"name": "MIT",
"name": "MIT",
"identifier": "MIT"
"identifier": "MIT"
@@ -170008,6 +170008,132 @@ export interface BrowserProviderJobState {
@@ -170008,6 +170008,132 @@ export interface BrowserProviderJobState {
} | null;
} | null;
}
}
+/** [email protected] (owner: run) */
+/**
+ * sandbox-provider.job-create-request@1 (PLANNED; plan T14 R4b, ADR-034): the body OpenVibe.Run POSTs to an external sandbox provider's (such as E2B or Fly Machines) job endpoint (capability sandbox_provider.job.dispatch). ID is the platform.job@1 id (job_<ULID>) and is also the idempotency key: a repeat POST with the same id answers the first job (200, created false) and never starts a second. CLASS is code or linux, the platform.runtime-class@1 classes a sandbox hosts. ARGS is the job's declared input as JSON (Run refuses more than 512 KiB serialized before it ever sends). INPUTS are files, each an OpenVibe.Media object pinned by sha256; never a URL, so the provider fetches nothing a caller chose. LIMITS are the hard caps of platform.job@1 (wall_ms required; cpu_ms and mem_bytes when the provider can enforce them). EGRESS is the network the job may reach, the values of OpenVibe.Node's worker.egress, as platform.job@1 `net` carries it. The request carries no project, subject, requester, token or other credential: the provider never learns who pays, and Run authenticates to it with a key from Run's own environment, sent in a header and never in this body. The provider's answer is sandbox-provider.job-create-result@1.
+ */
+export interface SandboxProviderJobCreateRequest {
+ /**
+ * platform.job@1 `id`
+ */
+ id: string;
+ class: "code" | "linux";
+ /**
+ * The job's declared input as JSON; Run enforces the 512 KiB cap before sending.
+ */
+ args: {};
+ /**
+ * Files for the job, each an OpenVibe.Media object pinned by sha256; names are unique; empty when the job has none.
+ *
+ * @maxItems 32
+ */
+ inputs: {
+ /**
+ * File name in the job directory; no path separators.
+ */
+ name: string;
+ media_id: string;
+ /**
+ * Lowercase hex SHA-256 of the object's bytes.
+ */
+ sha256: string;
+ size_bytes?: number;
+ }[];
+ /**
+ * Caps on the running job (platform.job@1 limits); the provider ends a job that hits one with exit_reason limit.
+ */
+ limits: {
+ /**
+ * Running time from the job's start
+ */
+ wall_ms: number;
+ /**
+ * CPU time, user plus system; when the provider can enforce it
+ */
+ cpu_ms?: number;
+ /**
+ * Peak resident memory; when the provider can enforce it
+ */
+ mem_bytes?: number;
+ };
+ /**
+ * OpenVibe.Node worker.egress values: none (no network), public (the internet without private, link-local or the network's own ranges), openvibe-only (the network's own endpoints only).
+ */
+ egress: "none" | "public" | "openvibe-only";
+}
+
+/** [email protected] (owner: run) */
+/**
+ * sandbox-provider.job-create-result@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) answer to OpenVibe.Run's job POST (sandbox-provider.job-create-request@1): 201 { job, created: true } when it accepted a new job, 200 { job, created: false } for an idempotent repeat of an id it already holds (the first job's state, nothing started twice). JOB is sandbox-provider.job-state@1 and Run validates it before adopting any of it. A refused POST answers a problem instead (4xx; 5xx is retried by Run on its next tick), never this contract. For the later calls, GET <provider>/jobs/:id answers sandbox-provider.job-state@1 and POST <provider>/jobs/:id/cancel answers it too; a 404 on either means the provider does not know the job.
+ */
+export interface SandboxProviderJobCreateResult {
+ job: SandboxProviderJobState;
+ /**
+ * True when this POST created the job (201); false for an idempotent repeat (200).
+ */
+ created: boolean;
+}
+
+/** [email protected] (owner: run) */
+/**
+ * sandbox-provider.job-state@1 (PLANNED; plan T14 R4b, ADR-034): an external sandbox provider's (such as E2B or Fly Machines) state of one job, the body of GET <provider>/jobs/:id and the `job` of sandbox-provider.job-create-result@1. OpenVibe.Run validates it before adopting any of it: a malformed state is never adopted, and the job stays as Run last saw it. A 404 on GET or on cancel means the provider does not know the job (Run fails the job run.job.worker_failed). STATE is queued, running, then one end state: succeeded, failed, cancelled or expired. WALL_MS is the provider's CLAIM of the time the job ran, nothing more: Run bills from its own observed duration and clamps a larger claim (observed plus 5 s), so this field never sets a price. STARTED_MS and FINISHED_MS are the provider's clock, Unix ms, null until they happen. EXIT_REASON uses the reasons of platform.job-frame@1 `job_exit` (exited, cancelled, ttl, limit, stopped, failed) and is null until the job ended. EXIT_CODE is the job's exit status, null when it was killed or never ran. RESULT is the declared output (an object, at most 256 KiB serialized), null unless the job succeeded. STDOUT is the captured output in the ring shape OpenVibe.Bot returns (bot.job-dispatch-result@1) so Run's poller streams it the same way, or null when there is none. There is no node, project or class field: a provider cannot name a node or a project.
+ */
+export interface SandboxProviderJobState {
+ /**
+ * platform.job@1 `id`
+ */
+ id: string;
+ /**
+ * queued (accepted, not started), running, then one end state: succeeded (exited 0), failed (another exit code, a limit, or the provider could not run it), cancelled (a cancel), expired (the job's lifetime ran out).
+ */
+ state: "queued" | "running" | "succeeded" | "failed" | "cancelled" | "expired";
+ /**
+ * When the job started, provider's clock, Unix ms; null until it started.
+ */
+ started_ms: number | null;
+ /**
+ * When the job ended, provider's clock, Unix ms; null until it ended.
+ */
+ finished_ms: number | null;
+ /**
+ * The provider's CLAIM of the running time; Run clamps it to its own observed duration plus 5 s and never bills above that. 0 while the job has not run.
+ */
+ wall_ms: number;
+ /**
+ * Why the job ended, as platform.job-frame@1 job_exit reason; null until it ended.
+ */
+ exit_reason: "exited" | "cancelled" | "ttl" | "limit" | "stopped" | "failed" | null;
+ /**
+ * The script's exit status; null when it was killed or never ran.
+ */
+ exit_code: number | null;
+ /**
+ * The declared output as a JSON object, at most 256 KiB serialized; null unless the job succeeded.
+ */
+ result: {} | null;
+ /**
+ * The captured stdout, or null when the job has none.
+ */
+ stdout: {
+ /**
+ * The held chunks joined in order, at most 1 MiB.
+ */
+ text: string;
+ /**
+ * The chunk_seq of the first held chunk; null when none is held.
+ */
+ first_seq: number | null;
+ /**
+ * The highest chunk_seq seen for the job; 0 when none.
+ */
+ last_seq: number;
+ /**
+ * True when an earlier chunk fell off the ring.
+ */
+ truncated: boolean;
+ } | null;
+}
+
/** [email protected] (owner: run) */
/** [email protected] (owner: run) */
/**
/**
* run.job-create-request@1 (PLANNED; plan T14 R1, ADR-034): the body of POST /api/v1/jobs on OpenVibe.Run (capability run.job.submit). The job belongs to the project the caller's token names, or, for a first-party service acting for a person (X-OV-Subject), the project it names with X-OV-Project once Network confirms the person is an admin or owner of it (ADR-003 amendment 2026-10-12); the person is then the requester and the service is recorded as via. CLASS is a platform.runtime-class@1 name; Run accepts any of them but places a job only on a node that advertises worker:<class> (platform.resource-offer@1), so a class no node runs today (browser, linux, desktop, gpu) ends expired with run.job.unplaceable. ARTIFACT is the pre-registered artifact to run (name and one exact version), required for function and code, as platform.job@1 carries it. ARGS is the artifact's declared input as JSON (at most 512 KiB serialized, else 413). INPUTS are files placed in the job directory before the process starts, each an OpenVibe.Media object pinned by its sha256: a size or digest mismatch fails the job (run.job.input_digest_mismatch) before the process ever runs; Run never fetches a caller-chosen URL. LIMITS are hard caps merged stricter with the node's own (platform.job@1 limits); a request over the project's tier cap is refused 422 run.limits.exceeded, never silently lowered. EGRESS is the network the job may reach, the values of OpenVibe.Node's worker.egress, carried per job as the same-named platform.job@1 net: none (default; no network), public or openvibe-only. A job asking for public or openvibe-only is placed only on a node whose worker.egress allows it; a worker whose host policy is stricter runs the stricter one. REQUIREMENTS (platform.workload-requirements@1) narrow placement; absent means { kind: run.<class>, mobility: job, latency_class: background, objective: balanced }. IDEMPOTENCY_KEY: a repeat from the same project within 24 h with an identical body answers the first job (200, created false); with a different body 409 run.idempotency.conflict.
* run.job-create-request@1 (PLANNED; plan T14 R1, ADR-034): the body of POST /api/v1/jobs on OpenVibe.Run (capability run.job.submit). The job belongs to the project the caller's token names, or, for a first-party service acting for a person (X-OV-Subject), the project it names with X-OV-Project once Network confirms the person is an admin or owner of it (ADR-003 amendment 2026-10-12); the person is then the requester and the service is recorded as via. CLASS is a platform.runtime-class@1 name; Run accepts any of them but places a job only on a node that advertises worker:<class> (platform.resource-offer@1), so a class no node runs today (browser, linux, desktop, gpu) ends expired with run.job.unplaceable. ARTIFACT is the pre-registered artifact to run (name and one exact version), required for function and code, as platform.job@1 carries it. ARGS is the artifact's declared input as JSON (at most 512 KiB serialized, else 413). INPUTS are files placed in the job directory before the process starts, each an OpenVibe.Media object pinned by its sha256: a size or digest mismatch fails the job (run.job.input_digest_mismatch) before the process ever runs; Run never fetches a caller-chosen URL. LIMITS are hard caps merged stricter with the node's own (platform.job@1 limits); a request over the project's tier cap is refused 422 run.limits.exceeded, never silently lowered. EGRESS is the network the job may reach, the values of OpenVibe.Node's worker.egress, carried per job as the same-named platform.job@1 net: none (default; no network), public or openvibe-only. A job asking for public or openvibe-only is placed only on a node whose worker.egress allows it; a worker whose host policy is stricter runs the stricter one. REQUIREMENTS (platform.workload-requirements@1) narrow placement; absent means { kind: run.<class>, mobility: job, latency_class: background, objective: balanced }. IDEMPOTENCY_KEY: a repeat from the same project within 24 h with an identical body answers the first job (200, created false); with a different body 409 run.idempotency.conflict.
new file mode 100644
new file mode 100644
@@ -0,0 +1,18 @@
@@ -0,0 +1,18 @@
+{
+ "id": "sandbox_provider.job.dispatch",
+ "version": "1.0",
+ "owner": "run",
+ "status": "planned",
+ "visibility": "internal",
+ "sensitive": true,
+ "description": "PLANNED (plan T14 R4b, ADR-034): OpenVibe.Run's outbound call to the external sandbox provider (such as E2B or Fly Machines) it places class code and linux jobs on: POST <provider>/jobs {id, class, args, inputs, limits, egress} (idempotent by job id; 201 created, 200 repeat), GET <provider>/jobs/:id for the state, POST <provider>/jobs/:id/cancel to stop it; a 404 on the last two means the provider does not know the job. The request carries no project, subject or credential: the provider never learns who pays. Run authenticates with a key from its own environment, never from an offer and never logged, validates every answer against sandbox-provider.job-state@1 (a malformed state is never adopted), and bills from its own observed duration, clamping the provider's claimed wall_ms to that plus 5 s. Run's own outbound call; no token grants it and it is never delegated.",
+ "permissions": [],
+ "resourceConstraints": [
+ "project"
+ ],
+ "quotaClass": "bot-jobs",
+ "events": [],
+ "implementedBy": [],
+ "inputSchema": "sandbox-provider.job-create-request@1",
+ "outputSchema": "sandbox-provider.job-create-result@1"
+}
@@ -1,12 +1,12 @@
@@ -1,12 +1,12 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.124.0",
+ "version": "0.159.0",
"lockfileVersion": 3,
"lockfileVersion": 3,
"requires": true,
"requires": true,
"packages": {
"packages": {
"": {
"": {
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.124.0",
+ "version": "0.159.0",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -1,6 +1,6 @@
@@ -1,6 +1,6 @@
{
{
"name": "openvibe-contracts",
"name": "openvibe-contracts",
- "version": "0.158.0",
+ "version": "0.159.0",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"description": "Machine-readable contracts for the OpenVibe network: schemas, manifests, ids, error envelope, capability checks.",
"main": "index.js",
"main": "index.js",
"types": "generated/typescript/index.d.ts",
"types": "generated/typescript/index.d.ts",
@@ -122,4 +122,16 @@ const dispatch = contracts.capabilities.get('browser_provider.job.dispatch');
@@ -122,4 +122,16 @@ const dispatch = contracts.capabilities.get('browser_provider.job.dispatch');
ok(dispatch && dispatch.owner === 'run' && dispatch.status === 'planned' && dispatch.visibility === 'internal' && dispatch.sensitive === true && dispatch.implementedBy.length === 0 && dispatch.permissions.length === 0, 'browser_provider.job.dispatch is a planned internal sensitive capability of Run');
ok(dispatch && dispatch.owner === 'run' && dispatch.status === 'planned' && dispatch.visibility === 'internal' && dispatch.sensitive === true && dispatch.implementedBy.length === 0 && dispatch.permissions.length === 0, 'browser_provider.job.dispatch is a planned internal sensitive capability of Run');
ok(dispatch.inputSchema === 'browser-provider.job-create-request@1' && dispatch.outputSchema === 'browser-provider.job-create-result@1' && !run.capabilities.includes('browser_provider.job.dispatch'), 'it names the two contracts and is not yet a capability Run serves');
ok(dispatch.inputSchema === 'browser-provider.job-create-request@1' && dispatch.outputSchema === 'browser-provider.job-create-result@1' && !run.capabilities.includes('browser_provider.job.dispatch'), 'it names the two contracts and is not yet a capability Run serves');
+// Run's outbound call to an external sandbox provider (T14 R4b): the browser-provider shape for classes code and linux.
+const sp = id => contracts.schema(`sandbox-provider.${id}`);
+for (const id of ['job-create-request', 'job-state', 'job-create-result']) {
+ ok(sp(id).additionalProperties === false, `sandbox-provider.${id} refuses unknown fields`);
+ if (id !== 'job-create-result') assert.deepStrictEqual(Object.keys(sp(id).properties).filter(k => k !== 'class'), Object.keys(bp(id).properties).filter(k => k !== 'class'));
+}
+checks += 2;
+assert.deepStrictEqual(sp('job-create-request').properties.class.enum, ['code', 'linux']);
+checks++;
+const sandboxDispatch = contracts.capabilities.get('sandbox_provider.job.dispatch');
+ok(sandboxDispatch && sandboxDispatch.owner === 'run' && sandboxDispatch.status === 'planned' && sandboxDispatch.visibility === 'internal' && sandboxDispatch.sensitive === true && sandboxDispatch.implementedBy.length === 0 && sandboxDispatch.inputSchema === 'sandbox-provider.job-create-request@1' && sandboxDispatch.outputSchema === 'sandbox-provider.job-create-result@1' && !run.capabilities.includes('sandbox_provider.job.dispatch'), 'sandbox_provider.job.dispatch is a planned internal sensitive capability of Run naming the two contracts');
+
console.log(`platform run: ${checks} job, frame and metering checks passed`);
console.log(`platform run: ${checks} job, frame and metering checks passed`);