Xeonr Developer Docs
API SpecificationSandboxes

Create Sandbox

POST
/containers.api.v1.SandboxService/CreateSandbox

Creates a sandbox from an environment in the same namespace and starts its pod. Resolves the workload command (request argv, then the environment's default_argv, then the image ENTRYPOINT/CMD; exec_only runs nothing), creates any ephemeral workspace mounts, and applies the base egress policy (deny-all when none is given; the egress bootstrap defaults to 24h when max_lifetime_sec is 0). Rejects an environment in another namespace (PermissionDenied), platform-owned env names (InvalidArgument), both workspace_id and workspaces set, more than 8 mounts, a non-absolute or unclean working_dir, and a durable workspace already attached to another live sandbox (FailedPrecondition). Unimplemented without cluster access.

Access: namespace OPERATOR or above (write)

Authorization

BearerAuth
AuthorizationBearer <token>

A namespace-scoped API key (cpk_…) for machines, or a Xeonr Auth OIDC token for humans. The key's namespace and role are pinned server-side.

In: header

Header Parameters

Connect-Protocol-Version?number

Define the version of the Connect protocol

Connect-Timeout-Ms?number

Define the timeout, in ms

Request Body

application/json

namespace?string
Length1 <= length <= 64
environmentId?string
Length1 <= length <= 64
workspaceId?stringDeprecated

Deprecated: use workspaces. Treated as a single read-write mount at /workspace when set. Setting both is an error rather than a merge.

Lengthlength <= 128
workspaceCache?
runtimeEgress?|

steady-state; usually MODE_NONE

resources?
idleSuspendSec?integer

0 = never; validate:skip the server accepts any value (no bound applied)

maxLifetimeSec?integer

0 = none; validate:skip the server accepts any value (no bound applied)

label?string

user-defined display name (console-required)

Lengthlength <= 4096
env?

Workload env — same contract as RunJobRequest.env: pod-spec visible, not a secret channel, platform-owned names rejected.

Propertiesproperties <= 4096
workspaces?

The filesystems this sandbox should see. Each is durable, an immutable snapshot, or ephemeral — see WorkspaceMount. Empty is legal: a sandbox with no mounts starts in its image's own WORKDIR with only /tmp writable. At most 8 mounts.

Itemsitems <= 8
workingDir?string

Where the workload starts. Absolute; empty selects the default, which differs by primitive on purpose:

sandbox the first read-write mount — an interactive user has to land somewhere writable job the first read-write mount — inputs are already staged there app the IMAGE'S OWN WORKDIR. An App runs an image built elsewhere and that WORKDIR is part of its contract, so the platform does not override it just because the version happens to declare a writable mount.

Set it explicitly when the default is wrong: a monorepo mounted at /workspace whose build runs in /workspace/apps/web has no other way to say so, and relying on the default couples the working directory to the ORDER of the mount list, which is not something a caller expects to matter.

Lengthlength <= 4096
argv?argv

The command to run in this sandbox. Empty selects the environment's default_argv, then the image's own ENTRYPOINT/CMD — so a sandbox built on an image that starts a dev server starts it, rather than starting nothing.

BEHAVIOUR CHANGE: sandboxes previously ran a keepalive INSTEAD of the image's entrypoint, so an existing sandbox that was only ever exec'd into may now start running something. Set exec_only to keep the old behaviour.

Itemsitems <= 4096
execOnly?boolean

Run nothing: no default_argv, no image entrypoint, just a sandbox to exec into. This is the opt-out from the resolution above, and is NOT the same as an empty argv — empty means "resolve one", this means "there is none". validate:skip a bool has no bounds

restart?string

What to do with the COMMAND when it exits. Default NEVER. enum.defined_only = true

Value in"SANDBOX_RESTART_UNSPECIFIED" | "SANDBOX_RESTART_NEVER" | "SANDBOX_RESTART_ON_FAILURE" | "SANDBOX_RESTART_ALWAYS"
onExit?string

What to do with the SANDBOX once the run has ended and is not being restarted. Default SUSPEND_ON_SUCCESS. enum.defined_only = true

Value in"SANDBOX_ON_EXIT_UNSPECIFIED" | "SANDBOX_ON_EXIT_SUSPEND_ON_SUCCESS" | "SANDBOX_ON_EXIT_KEEP" | "SANDBOX_ON_EXIT_SUSPEND"
[key: string]?never

Response Body

application/json

application/json

curl -X POST "https://containers.xeonr.io/containers.api.v1.SandboxService/CreateSandbox" \  -H "Connect-Protocol-Version: 1" \  -H "Connect-Timeout-Ms: 1000" \  -H "Content-Type: application/json" \  -d '{}'
{
  "sandbox": {
    "sandboxId": "string",
    "namespace": "string",
    "environmentId": "string",
    "workspaceId": "string",
    "state": "SANDBOX_STATE_UNSPECIFIED",
    "ports": [
      {
        "port": 0,
        "url": "string",
        "accessToken": "string",
        "auth": "AUTH_UNSPECIFIED"
      }
    ],
    "createdAtUnix": "string",
    "resources": {
      "cpuMillis": 0,
      "memoryMb": 0,
      "ephemeralMb": 0,
      "pids": 0
    },
    "idleSuspendSec": 0,
    "maxLifetimeSec": 0,
    "workspaceCache": {
      "policy": "POLICY_UNSPECIFIED",
      "writebackSec": 0
    },
    "egressGrant": {
      "mode": "MODE_UNSPECIFIED",
      "expiresAtUnix": "string",
      "reason": "string",
      "policy": {
        "mode": "MODE_UNSPECIFIED",
        "allow": [
          {
            "hostPattern": "string",
            "ports": [
              0
            ],
            "mode": "RULE_MODE_UNSPECIFIED",
            "pathPrefixes": [
              "string"
            ],
            "methods": [
              "string"
            ],
            "maxResponseBytes": "string",
            "allowPlainHttp": true
          }
        ],
        "presets": [
          "string"
        ],
        "allowPlainHttp": true
      },
      "id": "string"
    },
    "runtimeEgress": {
      "mode": "MODE_UNSPECIFIED",
      "allow": [
        {
          "hostPattern": "string",
          "ports": [
            0
          ],
          "mode": "RULE_MODE_UNSPECIFIED",
          "pathPrefixes": [
            "string"
          ],
          "methods": [
            "string"
          ],
          "maxResponseBytes": "string",
          "allowPlainHttp": true
        }
      ],
      "presets": [
        "string"
      ],
      "allowPlainHttp": true
    },
    "label": "string",
    "egressGrants": [
      {
        "mode": "MODE_UNSPECIFIED",
        "expiresAtUnix": "string",
        "reason": "string",
        "policy": {
          "mode": "MODE_UNSPECIFIED",
          "allow": [
            {
              "hostPattern": "string",
              "ports": [
                0
              ],
              "mode": "RULE_MODE_UNSPECIFIED",
              "pathPrefixes": [
                "string"
              ],
              "methods": [
                "string"
              ],
              "maxResponseBytes": "string",
              "allowPlainHttp": true
            }
          ],
          "presets": [
            "string"
          ],
          "allowPlainHttp": true
        },
        "id": "string"
      }
    ],
    "env": {
      "property1": "string",
      "property2": "string"
    },
    "workspaces": [
      {
        "snapshotId": "string",
        "mountPath": "string",
        "readOnly": true,
        "workspaceId": "string",
        "ephemeral": true,
        "ephemeralFromSnapshotId": "string",
        "volume": {
          "sizeMb": "string",
          "seedSnapshotId": "string",
          "cacheKey": "string"
        }
      }
    ],
    "workingDir": "string",
    "argv": [
      "string"
    ],
    "restart": "SANDBOX_RESTART_UNSPECIFIED",
    "onExit": "SANDBOX_ON_EXIT_UNSPECIFIED",
    "runState": "RUN_STATE_UNSPECIFIED",
    "run": 0,
    "lastExitCode": 0
  }
}
{
  "code": "not_found",
  "message": "string",
  "details": [
    {
      "type": "string",
      "value": "string",
      "debug": {}
    }
  ]
}