---
title: "codes.sandbox-session@1 · OpenVibe.Services"
canonical: "https://openvibe.services/docs/contracts/codes.sandbox-session"
updated: null
---

# codes.sandbox-session@1 · OpenVibe.Services

HTML: [codes.sandbox-session@1 · OpenVibe.Services](https://openvibe.services/docs/contracts/codes.sandbox-session)

# CodesSandboxSession `codes.sandbox-session@1`

Generated at 2026-11-13 06:25:48 UTC from [`openvibe-contracts v0.419.0`](https://github.com/OpenVibers/OpenVibe.Contracts/tree/v0.419.0) and [`openvibe-sdk v0.161.0`](https://github.com/OpenVibers/OpenVibe.SDK/tree/v0.161.0).

- Version: 1.0.0
- Owner: codes
- Visibility: first-party
- Status: active
- Compatibility: backward
- Schema: [`https://openvibe.network/contracts/codes/sandbox-session.v1.json`](/docs/contracts/codes.sandbox-session.json)

codes.sandbox-session@1 (the Agent Streams plan, S6): the sandbox desk a chat plays session runs on. The desk is started with \`--shared\`: no profile, no keys, no person's files, public egress only (no private address is reachable) and a fresh workspace, and it is wiped at the end of the session. The owner sets the time box (5 to 30 minutes) and the cap (money and minutes), and arms the session with Go before the first request may run. The scratch site is a throwaway OpenVibe.Space address that is removed after 24 hours unless the owner keeps it.

## Fields

| Field | Type | Required | Description | Constraints |
| --- | --- | --- | --- | --- |
| `session_id` | string | yes | The sandbox session's id (ULID, Crockford base32). | - `pattern ^sbs_[0-9A-HJKMNP-TV-Z]{26}$` |
| `task` | object | yes | The task the desk is running for the queue. | - `no other fields` |
| `task.id` | string | yes | The task's id in Codes. | - `minLength 1`<br>- `maxLength 200` |
| `task.title` | string | yes |  | - `minLength 1`<br>- `maxLength 200` |
| `owner` | string | yes | The person the session spends their own allowance for. | - `pattern ^usr_[0-9A-HJKMNP-TV-Z]{26}$` |
| `state` | enum | yes | Where the desk stands: idle (nothing yet), armed (the owner pressed Go, the first request may run), running, paused, wiped (Wipe now reset the sandbox) or ended. | - `one of "idle", "armed", "running", "paused", "wiped", "ended"` |
| `time_box_minutes` | integer | yes | How long one request may run, in minutes (the owner sets 5 to 30; the default is 10). | - `minimum 5`<br>- `maximum 30` |
| `step_budget` | integer | yes | How many steps one request may take. | - `minimum 1` |
| `cap` | object | yes | The owner's cap for the session: the money and the minutes it may spend. When it is spent, the queue closes. | - `no other fields` |
| `cap.money_cents` | integer | yes | The cap in cents. | - `minimum 0` |
| `cap.minutes` | integer | yes | The cap in minutes of work. | - `minimum 1` |
| `spend` | object | yes | What the session has spent so far. | - `no other fields` |
| `spend.money_cents` | integer | yes |  | - `minimum 0` |
| `spend.seconds` | integer | yes |  | - `minimum 0` |
| `spend.steps` | integer | yes |  | - `minimum 0` |
| `armed` | boolean | yes | Whether the owner pressed Go. The first request of a session waits for it. |  |
| `armed_at` | string \| null |  | When the owner pressed Go. | - `format date-time` |
| `started_at` | string \| null |  |  | - `format date-time` |
| `ended_at` | string \| null |  |  | - `format date-time` |
| `wiped_at` | string \| null |  | When Wipe now reset the sandbox. | - `format date-time` |
| `egress_public_only` | const | yes | Always true: the sandbox reaches public addresses only, and no private address is reachable. | - `= true` |
| `scratch_site` | object \| null |  | The throwaway site the run left, or null before the first one. |  |

## Examples

From the contract's own test fixtures: valid ones validate, rejected ones must fail.

 Valid: armed

~~~~
{
  "session_id": "sbs_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "task": {
    "id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
    "title": "Chat plays"
  },
  "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "armed",
  "time_box_minutes": 10,
  "step_budget": 40,
  "cap": {
    "money_cents": 500,
    "minutes": 30
  },
  "spend": {
    "money_cents": 0,
    "seconds": 0,
    "steps": 0
  },
  "armed": true,
  "armed_at": "2026-11-06T20:00:00.000Z",
  "started_at": null,
  "ended_at": null,
  "wiped_at": null,
  "egress_public_only": true,
  "scratch_site": null
}
~~~~

Valid: running

~~~~
{
  "session_id": "sbs_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "task": {
    "id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
    "title": "Chat plays"
  },
  "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "running",
  "time_box_minutes": 10,
  "step_budget": 40,
  "cap": {
    "money_cents": 500,
    "minutes": 30
  },
  "spend": {
    "money_cents": 42,
    "seconds": 180,
    "steps": 6
  },
  "armed": true,
  "armed_at": "2026-11-06T20:00:00.000Z",
  "started_at": "2026-11-06T20:01:00.000Z",
  "ended_at": null,
  "wiped_at": null,
  "egress_public_only": true,
  "scratch_site": {
    "url": "https://scratch.openvibe.space/snake-4f2a",
    "expires_at": "2026-11-07T20:01:00.000Z",
    "kept": false
  }
}
~~~~

Rejected: egress-not-public-only

~~~~
{
  "session_id": "sbs_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "task": {
    "id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
    "title": "Chat plays"
  },
  "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "armed",
  "time_box_minutes": 10,
  "step_budget": 40,
  "cap": {
    "money_cents": 500,
    "minutes": 30
  },
  "spend": {
    "money_cents": 0,
    "seconds": 0,
    "steps": 0
  },
  "armed": true,
  "armed_at": "2026-11-06T20:00:00.000Z",
  "started_at": null,
  "ended_at": null,
  "wiped_at": null,
  "egress_public_only": false,
  "scratch_site": null
}
~~~~

Rejected: no-cap

~~~~
{
  "session_id": "sbs_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "task": {
    "id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
    "title": "Chat plays"
  },
  "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "armed",
  "time_box_minutes": 10,
  "step_budget": 40,
  "spend": {
    "money_cents": 0,
    "seconds": 0,
    "steps": 0
  },
  "armed": true,
  "armed_at": "2026-11-06T20:00:00.000Z",
  "started_at": null,
  "ended_at": null,
  "wiped_at": null,
  "egress_public_only": true,
  "scratch_site": null
}
~~~~

Rejected: time-box-out-of-range

~~~~
{
  "session_id": "sbs_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "task": {
    "id": "tsk_01JAB2C3D4E5F6G7H8J9K0MNPQ",
    "title": "Chat plays"
  },
  "owner": "usr_01JAB2C3D4E5F6G7H8J9K0MNPQ",
  "state": "armed",
  "time_box_minutes": 2,
  "step_budget": 40,
  "cap": {
    "money_cents": 500,
    "minutes": 30
  },
  "spend": {
    "money_cents": 0,
    "seconds": 0,
    "steps": 0
  },
  "armed": true,
  "armed_at": "2026-11-06T20:00:00.000Z",
  "started_at": null,
  "ended_at": null,
  "wiped_at": null,
  "egress_public_only": true,
  "scratch_site": null
}
~~~~

## Validate

~~~~
const contracts = require('openvibe-contracts');
contracts.validate('codes.sandbox-session@1', value);   // { valid, errors: [{ path, message }] }
~~~~
