openshell-cli — Claude Code skill: what it does, how to install
A skill that walks an agent through openshell commands: gateways, sandboxes, providers, profiles, policies, BYOC. Useful when the agent has to drive sandbox setup itself.
npx skills add NVIDIA/OpenShell --skill openshell-cli -a claude-codeWhat openshell-cli does
This skill covers openshell, the CLI for sandboxes and the platform objects around them. It lays out multi-step workflows: register a gateway, create a sandbox, manage providers and profiles, iterate on policy, expose services, run BYOC, route inference.
The order matters and the skill spells it out: openshell gateway add ... --local --name local, verify with openshell status and openshell whoami, then openshell sandbox create. Provider attachment is explicit — name each one with --provider, since a trailing command never selects it. --from-existing builds a provider from credentials already on the machine. Authoring the policy YAML is left to a companion skill, generate-sandbox-policy; this one stays on the command side. Anything the skill misses, the agent reads from the --help tree.
What you need beforehand: openshell on PATH and a reachable gateway on Docker, Podman, Kubernetes or the experimental VM driver. This is where it breaks — no gateway, no commands. Profiles are import-only, so a fresh gateway serves an empty catalog and --type matches nothing until an operator imports something. Run openshell profile list first, or your sandbox comes up without credentials.
What it helps with
- bring up a first sandbox against a local gateway
- build a provider from credentials already on the machine
- tell an auth failure apart from an unreachable gateway
- expose a service out of the sandbox with a port forward
- walk the policy lifecycle and apply a new revision
Example prompts for the agent
- Create a sandbox with
openshelland show me whatopenshell statusandopenshell whoamireturn - Make a provider named
my-githubof typegithubfrom existing credentials, then confirm it inopenshell profile list - Forward a port from the sandbox to localhost and explain which flags that takes
How to install openshell-cli
1Skills CLI (Vercel)
npx skills add NVIDIA/OpenShell --skill openshell-cli -a claude-code2Manually: put the files in place
~/.claude/skills/openshell-cli/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/openshell-cli/
1Skills CLI (Vercel)
npx skills add NVIDIA/OpenShell --skill openshell-cli -a codex2Manually: put the files in place
.agents/skills/openshell-cli/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add NVIDIA/OpenShell --skill openshell-cli -a cursor2Manually: put the files in place
.cursor/skills/openshell-cli/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install NVIDIA/OpenShell openshell-cli2Manually: put the files in place
.github/skills/openshell-cli/SKILL.md1Skills CLI (Vercel)
npx skills add NVIDIA/OpenShell --skill openshell-cli -a gemini-cli2Platform CLI
gemini skills install https://github.com/NVIDIA/OpenShell --path skills/openshell-cliManually: put the files in place
.devin/skills/openshell-cli/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add NVIDIA/OpenShell --skill openshell-cli -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about openshell-cli
How do I install openshell-cli in Claude Code?
Run npx skills add NVIDIA/OpenShell --skill openshell-cli -a claude-code. The skill file then lives at ~/.claude/skills/openshell-cli/SKILL.md.
What does openshell-cli do?
It teaches an agent to call openshell for gateways, sandboxes, providers, profiles, policies and BYOC flows. The agent follows known command sequences instead of guessing flags.
What has to be ready before the skill works?
openshell on PATH plus a reachable gateway on Docker, Podman, Kubernetes or the experimental VM driver. Docker is needed for BYOC local builds and Docker-backed gateways; for Kubernetes you need kubectl and Helm access.
Source text
SKILL.md · Apache-2.0
---
name: openshell-cli
description: Guide agents through using the OpenShell CLI (openshell) for sandbox management, gateway registration, provider configuration and refresh, profile management, policy iteration, settings, service exposure, BYOC workflows, and attached-provider inference. Covers basic through advanced multi-step workflows. Trigger keywords - openshell, sandbox create, sandbox exec, sandbox connect, logs, provider create, profile list, profile describe, provider refresh, policy set, policy get, settings, service expose, forward, port forward, BYOC, bring your own container, inference, use openshell, run openshell, CLI usage, manage sandbox, manage provider, gateway add, gateway select.
---
# OpenShell CLI
Guide agents through using the `openshell` CLI for sandbox and platform management -- from basic operations to advanced multi-step workflows.
## Overview
The OpenShell CLI (`openshell`) is the primary interface for managing sandboxes, providers, policies, settings, exposed services, and gateway registrations. Gateway service lifecycle is handled outside the CLI by packages, systemd, or Helm. This skill teaches agents how to orchestrate CLI commands for common and complex workflows.
**Companion skill**: For creating or modifying sandbox policy YAML content (network rules, L7 inspection, access presets), use the `generate-sandbox-policy` skill. This skill covers the CLI *commands* for the policy lifecycle; `generate-sandbox-policy` covers policy *content authoring*.
**Self-teaching**: The CLI has comprehensive built-in help. When you encounter a command or option not covered in this skill, walk the help tree:
```bash
openshell --help # Top-level commands
openshell <group> --help # Subcommands in a group
openshell <group> <cmd> --help # Flags for a specific command
```
This is your primary fallback. Use it freely -- the CLI's help output is authoritative and always up-to-date.
## Prerequisites
- `openshell` is on the PATH. Follow the published [installation guide](https://docs.nvidia.com/openshell/latest/about/installation.md) when it is not installed.
- A reachable OpenShell gateway backed by Docker, Podman, Kubernetes, or the experimental VM driver
- Docker is running only when using BYOC local builds or a Docker-backed gateway
- For Kubernetes deployments: `kubectl` and Helm access to the target cluster
## Authoritative References
Use `openshell --help` and nested `--help` output as the authority for the installed CLI version. Use the published documentation for product concepts and supported workflows:
- [Manage gateways](https://docs.nvidia.com/openshell/latest/how-it-works/gateways/overview)
- [Manage sandboxes](https://docs.nvidia.com/openshell/latest/how-it-works/sandboxes/overview)
- [Manage providers](https://docs.nvidia.com/openshell/latest/how-it-works/providers/overview)
- [Profiles](https://docs.nvidia.com/openshell/latest/how-it-works/providers/profiles)
- [Sandbox policies](https://docs.nvidia.com/openshell/latest/how-it-works/policies/overview)
- [Inference routing](https://docs.nvidia.com/openshell/latest/how-it-works/inference)
---
## Workflow 1: Getting Started
Use this workflow when the user has a gateway endpoint and wants to get a sandbox running for the first time.
### Step 1: Register a gateway
```bash
openshell gateway add http://127.0.0.1:8080 --local --name local
```
Use an `http://` endpoint only for trusted local port-forwarding or a protected private path. For a gateway behind an authenticated reverse proxy, register its HTTPS endpoint with `openshell gateway add https://gateway.example.com`.
### Step 2: Verify the gateway
```bash
openshell status
openshell whoami
```
Confirm the gateway is reachable, authentication is valid or not required, and
the output shows a version. `Status: Connected` only proves the public health
endpoint is reachable; inspect the separate `Authentication` line before
running protected commands. `openshell whoami` reports the identity validated
by the gateway, including the subject an administrator uses for workspace
membership. Add `--output json` for automation.
### Step 3: Create a sandbox
The simplest way to get a sandbox running:
```bash
openshell sandbox create
```
This creates a sandbox whose canonical main process is `/bin/bash -l` and
attaches your terminal to that retained process. Add `--detach` to return after
the sandbox becomes ready without attaching.
An explicit trailing command is foreground even when stdin or stdout is not a
terminal. The CLI streams its stdout and stderr and reports the command's exit
status after output drains. A failure to deliver output makes the CLI report a
failure even if the command itself exited successfully; check the sandbox's
state before retrying work that might have side effects. Exit code 0 leaves a
retained sandbox in `Completed`; nonzero leaves it in `Error` with
`MainProcessFailed`. Use `--no-keep` to delete either result after output
drains, or `--detach` for a long-running service. Combine
`--detach --no-keep` when the gateway should run the service without a host
attachment and delete its sandbox after the service exits.
When supplying `--name`, use a portable DNS-1123 label: at most 63 lowercase alphanumeric or `-` characters, beginning and ending with an alphanumeric character. The Kubernetes driver rejects uppercase letters, underscores, dots, and other names that cannot become Kubernetes resource labels.
Provider attachment is explicit. Name each provider with `--provider`; the
trailing command does not select or attach one. If the named provider does not
exist but a profile with that ID is available, the CLI can create it from local
credentials:
```bash
openshell sandbox create --from registry.example.com/your-org/claude-agent:latest --provider claude-code -- claude
openshell sandbox create --from registry.example.com/your-org/codex-agent:latest --provider codex -- codex
```
The agent will be prompted interactively if credentials are missing.
### Step 4: Exit and clean up
Exit the sandbox shell with `exit`, or detach with Ctrl-D, then:
```bash
openshell sandbox delete <name>
```
---
## Workflow 2: Provider Management
Providers supply credentials and provider-specific configuration to sandboxes. Provider profiles are import-only: a gateway serves exactly what an operator imported, and a new gateway serves an empty catalog. Never rely on a hard-coded type list or on a legacy alias such as `gh` or `claude` — `--type` matches a profile ID exactly. Discover the profiles available on the selected gateway:
```shell
openshell profile list
openshell profile list --type provider --output json
```
### Create a provider from local credentials
```bash
openshell provider create --name my-github --type github --from-existing
```
The `--from-existing` flag discovers credentials from local state (e.g., `gh auth` tokens, Claude config files).
### Create a provider with explicit credentials
```bash
openshell provider create --name my-openai --type openai \
--credential OPENAI_API_KEY
```
Bare `KEY` reads the value from the environment variable of that name and avoids placing the secret in shell history. Use `KEY=VALUE` only when the user explicitly accepts that exposure.
Other credential sources are `--from-gcloud-adc` for compatible profiles and `--runtime-credentials` when the gateway or sandbox resolves the required credentials at runtime.
Static provider credentials resolve only for hosts, ports, and paths declared by
the provider profile. Use `profile export` to inspect that boundary
when a placeholder is present but requests receive
`credential_endpoint_mismatch`. A profileless static provider fails closed
because the gateway cannot construct a binding.
When an inspected request receives `request_authority_mismatch`, compare its
HTTP authority with the CONNECT tunnel endpoint. The host and effective port
must match. For a tunnel to `api.example.com:8443`, send
`Host: api.example.com:8443`; `Host: api.example.com` omits the non-default
port and is rejected. An absolute-form request target must use the same
authority.
Profile-backed providers always contribute policy unless a gateway-global
policy is active. Static credential endpoint binding remains independently
enforced.
### Label and select providers
Attach labels during provider creation with repeatable `--label KEY=VALUE`
flags. Labels work with each credential source. List selectors require every
comma-separated key-value pair to match and apply before pagination.
```shell
openshell provider create --name public-pypi --type pypi --label env=dev --label team=ml
openshell provider list --label-selector env=dev,team=ml
openshell provider list --all-workspaces --label-selector team=ml --output json
openshell provider get public-pypi
```
Use an imported profile, such as `pypi` in this example. `get` displays labels;
structured list output includes them. `--all-workspaces` requires Platform
Admin access. Preserve the selector and workspace scope when passing the next
`--page-token`. Labels cannot be changed or removed after provider creation.
### Inspect and manage provider profiles
```shell
openshell profile describe github
openshell profile export github --output yaml
openshell profile lint --file ./my-profile.yaml
openshell profile import --file ./my-profile.yaml
openshell profile lint --url https://example.com/profiles/my-profile.yaml
openshell profile import --url https://example.com/profiles/my-profile.yaml
```
`--url` accepts one HTTP or HTTPS YAML or JSON profile. Review its endpoint and
binary grants before importing it. The URL path must end in `.yaml`, `.yml`, or
`.json`; downloads are limited to 1 MiB and 15 seconds.
Use `profile describe` to inspect a definition's credential metadata, endpoints, TLS handling, MCP access settings, rule counts, binaries, source, and scope before creating a provider. Check for `tls: skip` and the uninspected-credential opt-in before relying on displayed L7 rules. List and describe accept table, JSON, and YAML output; use structured output for complete rule definitions, `--workspace` for a workspace catalog, or `--global` for platform scope. Use `profile export` when preparing an editable definition, `profile update <id> --file <file>` to replace an existing custom profile with its current resource version, and `profile delete <id>...` to remove custom profiles. Provider instances remain under `provider`.
Existing scripts can continue using `provider list-profiles` and `provider profile export/import/update/lint/delete`. These commands share the top-level handlers and preserve their arguments, output options, and workspace/global flags. Prefer `profile` when writing new commands.
### List, inspect, update, delete
Use `openshell sandbox provider status --help` and the attach, detach, and update help to find the installed version's wait options. Add `--wait` when the next step depends on a provider change taking effect. Without it, a successful command only confirms that the gateway saved the change. Save the returned `receipt_id` to check that same change later, and inspect the result for every selected sandbox. Credential refresh status confirms that OpenShell obtained credentials; provider status confirms that the sandbox applied them, activated the policy, and updated the environment for new processes. If the status is `superseded`, explain that a later change replaced the request and inspect that change separately.
If attach, detach, or update reports `CONFIG_OPERATION_STORAGE_UNCERTAIN`, explain that the change may already be saved and its readiness receipt may be unavailable. Do not blindly retry the mutation. Inspect the provider and sandbox state and reconcile the saved change before deciding on another mutation; the error proves neither rollback nor readiness.
```bash
openshell provider list
openshell provider list --output json
openshell provider get my-github
openshell provider update my-github --from-existing
openshell provider delete my-github
```
`provider update` does not take `--type`. It updates credentials, config, or credential expiry on the existing provider.
For an external updater whose workload must retain its original environment reference, use a credential profile with `stable_placeholder: true` and a gateway and supervisor that support that mode. The CLI checks gateway support before importing or updating the opt-in. The default remains `false`, preserving existing revision-scoped behavior and ordinary legacy gateway operation. Preserve the supervisor-issued value; never manufacture a placeholder. After the supervisor installs a value-only update, the retained reference resolves the current credential only at its effective host, port, and path binding. Replacement, expiry, changed binding, or an activated detach prevents old authority from resolving. Remove the opt-in and activate that change before downgrading the gateway. See the provider profiles documentation for the lifecycle contract and its separation from gateway-managed refresh and `token_grant`.
### Configure credential refresh
Use refresh commands only when the provider profile and gateway support refreshable credentials:
```bash
openshell provider refresh status my-provider
openshell provider refresh configure my-provider \
--credential-key MS_GRAPH_ACCESS_TOKEN \
--strategy oauth2-refresh-token \
--secret-material-env REFRESH_TOKEN=MS_GRAPH_REFRESH_TOKEN \
--credential-expires-at 2026-07-16T00:00:00Z
openshell provider refresh rotate my-provider --credential-key ACCESS_TOKEN
```
Prefer `--secret-material-env KEY[=ENVVAR]` for secret refresh material. `--material KEY=VALUE` is for non-secret material; `--secret-material-key` marks supplied material keys as secret.
The gateway stores secret refresh material through its active credential driver.
With Vault selected, refresh tokens, client secrets, and private keys live in
Vault alongside injectable provider credentials; refresh state contains only
opaque handles. A credential-backend read or write failure makes refresh fail
closed rather than falling back to inline storage. Before OpenShell 0.1.0, the
gateway does not migrate legacy inline refresh material or move secrets between
credential backends. Reconfigure affected grants after upgrading, and remove or
reconfigure credentials while the original backend remains available before
changing backends. Do not run mixed gateway versions against the same refresh
records.
Gateway-managed refresh credentials use an identity-stable workload handle.
Routine automatic refresh and `provider refresh rotate` update the access token
behind that handle, so long-running processes do not need to restart. Running
processes must be restarted once when upgrading from revision-scoped
placeholders. A later `provider refresh configure` call is an explicit
reauthorization boundary: it revokes the previous handle, and processes holding
that handle fail closed until restarted.
While gateway-managed refresh is configured, `provider update --credential`
cannot replace or delete the refresh-owned primary credential or any co-minted
output. Use `provider refresh rotate`, reconfigure refresh, or delete refresh
before returning those keys to manual management. Unrelated provider fields
remain updateable.
When OAuth refresh fails, inspect the `RECOVERY` and `FAILURE_CODE` columns from
`provider refresh status`; do not infer the remedy from HTTP status or parse
`LAST_ERROR`. `retry` means the worker will try again, `reauthorize` means the
user must obtain a new OAuth grant and run `provider refresh configure`,
`fix_configuration` means an operator must repair the OAuth client, scopes, or
administrator policy, and `investigate` can mean an unrecognized issuer
response or an interrupted mint with an uncertain outcome. The gateway parks
`reauthorize` records until a manual rotate or reconfiguration. It retries
`fix_configuration` records hourly so externally repaired configuration can
recover without rapid token-endpoint traffic. The existing access credential
remains usable only until its recorded expiry.
`refresh_in_progress` and `refresh_committing` can indicate active work. Check
gateway health and refresh status again before treating them as failures;
concurrent refresh requests wait for the active mint. If a gateway interruption
leaves either marker behind and rotation reports an uncertain previous mint,
the gateway will not reuse the grant automatically. Investigate the issuer state,
then run `provider refresh configure` with a valid grant. Obtain a new OAuth
grant first if the previous refresh token may have been consumed. Do not clear
the marker directly or repeatedly rotate it; reconfiguration revokes old
workload handles, so restart processes holding them.
---
## Workflow 3: Sandbox Lifecycle
### Create with options
```bash
openshell sandbox create \
--name my-sandbox \
--provider my-github \
--provider my-claude \
--policy ./my-policy.yaml \
--label team=agents \
-- claude
```
Key flags:
- `--provider`: Attach configured credential providers for API keys, tokens, and other secrets (repeatable)
- `--policy`: Custom policy YAML (otherwise uses built-in default or `OPENSHELL_SANDBOX_POLICY` env var)
- `--gpu [COUNT]`: Request the driver's default GPU selection or a specific GPU count
- `--cpu`, `--memory`: Set per-sandbox compute sizing. Docker/Podman apply limits; Kubernetes applies matching requests and limits.
- `--driver-config-json`: Pass experimental driver-specific sandbox configuration
- `--template NAME`: Create from a named sandbox workload template. Conflicts with inline workload flags such as `--from`, `--gpu`, `--cpu`, `--memory`, `--env`, `--env-from`, and `--driver-config-json`.
- `--label KEY=VALUE`: Add labels for later selection (repeatable)
- `--annotation KEY=VALUE`: Add non-secret metadata such as file paths (repeatable)
- `--env KEY=VALUE`: Set non-secret sandbox environment variables (repeatable); use `--provider` for credentials
- `--env-from KEY[=ENVVAR]`: Read a sandbox environment value from the CLI process environment without putting its value in arguments; use `--provider` to keep credentials hidden from the workload
- `--tty`: Allocate a retained PTY for the canonical main process
- `--restart-policy never|on-failure|always`: Select gateway-owned main-process restart behavior; `never` is the default
- `--approval-mode manual|auto`: Control handling of agent-authored policy proposals; `manual` is the default
- `--upload <PATH>[:<DEST>]`: Upload local files into the container working directory or an explicit destination
- `--no-git-ignore`: Disable `.gitignore` filtering for uploads
- `--no-keep`: Delete the sandbox after main output and the exit result drain
- `--detach`: Start the canonical main process without attaching
- `--forward [BIND_ADDRESS:]PORT`: Forward a local port and keep the sandbox alive
- `--editor vscode|cursor`: Open a remote editor after creation and keep the sandbox alive
If the driver rejects foreground attachment during create validation, use `--detach`.
`--detach` adds no attachment grace period. When the canonical process exits,
its terminal phase is reported immediately. A foreground create declares one
expected main-process SSH attachment; cleanup finalizes after that connection
closes naturally. With `--detach --no-keep`, the gateway owns the detached
process lifecycle and deletes the ephemeral sandbox after terminal reporting
finishes.
Do not combine `--upload` with a trailing main command. Uploads currently finish
after the canonical process starts; create a scratch sandbox and use
`sandbox exec`, or build the files into the image.
Create from a reusable workload template when several sandboxes should share
image, environment, sizing, or driver-specific configuration:
```bash
openshell sandbox template create gpu-kata \
--image registry.example.com/agents/python:latest \
--cpu 2 \
--memory 4Gi \
--gpu 1 \
--driver-config-json '{"kubernetes":{"pod":{"node_selector":{"pool":"gpu"}}}}'
openshell sandbox create --name my-sandbox --template gpu-kata --provider my-github -- claude
```
Driver config is disabled by default. These template and one-off
`sandbox create --driver-config-json` examples require the administrator to set
`allow_driver_config = true` for the selected driver. This does not waive
resource admission: external attachments need administrator-controlled approval
labels on the actual resources, not sandbox labels. GPU device attachments
are temporarily exempt from labels; the public `--gpu` flag needs no driver
config opt-in. Consult the published gateway configuration reference before
changing admission settings; do not recommend disabling admission to bypass a
denial. Put driver config on a template only when it should be reused.
### Manage sandbox workload templates
```bash
openshell sandbox template create gpu-kata \
--image registry.example.com/agents/python:latest \
--cpu 2 \
--memory 4Gi \
--gpu 1 \
--label team=runtime \
--env FEATURE_FLAG=on
openshell sandbox template list
openshell sandbox template list --label-selector team=runtime
openshell sandbox template list --all-workspaces --output json
openshell sandbox template get gpu-kata
openshell sandbox template delete gpu-kata
```
Template `--image` accepts an OCI image reference. If omitted, the gateway
applies its default sandbox image when creating a sandbox from the template.
Create-time policy, providers, labels, uploads, forwarding, editor launch, and
the initial command stay on `sandbox create`.
### List and inspect sandboxes
```bash
openshell sandbox list
openshell sandbox list --selector team=agents --output json
openshell sandbox get my-sandbox
```
Most commands with an optional sandbox name use the last-used sandbox. Pass an explicit name in automation.
### Connect to a running sandbox
```bash
openshell sandbox connect my-sandbox
openshell sandbox connect my-sandbox --editor vscode
```
Attaches to the sandbox's existing canonical main process. Disconnecting leaves
that process running; reconnecting targets the same process instance and replays
recent output. If an established SSH transport is interrupted, such as when a
laptop sleeps and wakes, the CLI retries transient failures for up to 60 seconds
and reattaches to that same process. Use `sandbox exec --tty -- /bin/bash -l`
for a new shell. Press `Ctrl-D` or `Ctrl-P`, then `Ctrl-Q` to disconnect without
terminating main. OpenSSH's `~.` escape looks like transport loss and therefore
starts automatic recovery; after it reattaches, use `Ctrl-D` or `Ctrl-P`, then
`Ctrl-Q` to exit, or press `Ctrl-C` between retry attempts to cancel recovery.
When you own stdin, `Ctrl-C` interrupts the foreground process. In a read-only
attachment, `Ctrl-C` or `Ctrl-D` exits the viewer and leaves main and other
attachments running. Configure VS Code Remote-SSH with:
```bash
openshell sandbox ssh-config my-sandbox >> ~/.ssh/config
```
A writable attachment that finds another attachment holding stdin reports `attached read-only; retry input after the owner disconnects`. Automatic recovery can hit this when it reattaches before the supervisor closes the dead connection. The supervisor closes a connection 60 seconds after it last received bytes from it, which can be later than 60 seconds after the network failed if the relay buffered data. After the old owner disconnects or times out, send the input you meant to type next. If stdin is free, the attachment prints `input enabled` and forwards that input to the process, so do not probe with Enter or a prompt answer such as `y`. If nothing prints, the old connection still holds stdin. Input sent while the attachment was read-only never reaches the process, so send it again later. `Ctrl-C`, `Ctrl-D`, and `Ctrl-P` then `Ctrl-Q` still exit a read-only attachment instead of enabling input; enable input first if you need `Ctrl-C` to interrupt the process. Recovery never takes stdin from a healthy owner, and an explicitly read-only attachment stays read-only.
If `connect` reports `canonical main process already finished`, inspect the
result with `sandbox get`. A pending foreground attachment can still retrieve
retained output in `Completed` or `Error`; phase alone does not determine
whether attachment is available. A nonzero main-process exit under
`on-failure`, or any exit under `always`,
moves the sandbox to `Starting` during backoff and resource replacement. Connect
and exec commands resume after the new supervisor session makes it `Ready`.
An explicit `sandbox stop` cancels a pending restart.
### Upload and download files
```bash
# Upload local files to the sandbox working directory
openshell sandbox upload my-sandbox ./src
# Download a path relative to the sandbox working directory
openshell sandbox download my-sandbox output ./local-output
```
Uploads use bounded streaming and wait for sandbox input consumption; the 4 MiB process-input budget is not a total upload-size limit. Upgrade both the CLI and the supervisor runtime used by the sandbox. An older supervisor rejects the upload protocol before file data is sent. Failed or canceled uploads may leave incomplete files in the destination; check it before retrying or remove the partial files.
Uploads inside a Git work tree honor `.gitignore` by default and stop if Git filtering fails or selects no files. Sources confirmed to be outside a Git work tree upload without filtering, with a warning that `.gitignore` rules are not applied. Git must be available to determine whether filtering applies; broken or inaccessible repositories stop the upload. Add `--no-git-ignore` for an intentional unfiltered upload. This also applies to `sandbox create --upload`.
`sandbox create --upload` checks every upload locally before provisioning and creates no sandbox if one is rejected. If a transfer fails after provisioning, the sandbox remains running and earlier uploads may have completed. Retry with `sandbox upload` against that sandbox, or remove it with `sandbox delete`.
Uploads preserve symlinks, including dangling symlinks, instead of dereferencing their targets. A symlink source bypasses Git-aware filtering so the link itself is archived.
When the upload destination is omitted, the CLI discovers the remote working
directory. Uploading a named directory merges it into an existing directory of
the same name, overwriting matching entries without deleting unrelated entries.
Downloads accept paths relative to that working directory or absolute paths
within it.
### Execute a non-interactive command
```bash
openshell sandbox exec my-sandbox --workdir /workspace -- ls -la
openshell sandbox exec --name my-sandbox --env MODE=test -- cargo test
```
The sandbox is a positional name or `--name`, not both; omit it to use the
last-used sandbox. `--` is required and everything after it is the remote
command, so put options such as `--tty` before it.
`sandbox exec` starts an independent sibling process and streams output. After
stdout and stderr drain, it returns the remote command's exit code if delivery
succeeds. Output delivery failure instead returns exit code 74, even when the
command exited successfully. A descendant that keeps an inherited output pipe
open for more than 30 seconds after the command exits triggers that failure.
Check whether the command ran before retrying work with side effects. Use
`sandbox connect` to attach to the canonical main process.
Use `--env` only for non-secret values. Attach credentials to the sandbox with a
provider instead of passing API keys, tokens, or other secrets to `sandbox exec`.
### Change attached providers
```bash
openshell sandbox provider list my-sandbox
openshell sandbox provider list my-sandbox --output json
openshell sandbox provider attach my-sandbox my-github --wait --timeout 30
openshell sandbox provider status my-sandbox my-github --output json
openshell sandbox provider detach my-sandbox my-github --wait --timeout 30
```
Structured attachment output contains provider names, types, and sorted
credential and config key names. It never contains credential, handle, or
config values.
### View logs
```bash
# Recent logs
openshell logs my-sandbox
# Stream live logs
openshell logs my-sandbox --tail
# Filter by source and level
openshell logs my-sandbox --tail --source sandbox --level warn
# Logs from the last 5 minutes
openshell logs my-sandbox --since 5m
```
### Delete sandboxes
```bash
openshell sandbox delete my-sandbox
openshell sandbox delete sandbox-1 sandbox-2 sandbox-3 # Multiple at once
openshell sandbox delete --all
```
`deletion accepted` means cleanup is still pending. Inspect the sandbox until
it disappears before assuming completion. An already-absent sandbox succeeds;
missing workspaces and authorization failures remain errors. Do not blindly
retry by name if another process might have recreated that name.
### Stop and start sandboxes
Use stop to halt compute while retaining the sandbox and its persistent
workspace:
```bash
openshell sandbox stop [name]
openshell sandbox start [name]
```
Both commands default to the last-used sandbox. Stop stops background
forwards and waits for `Stopped`; start waits for `Ready`. Connect, exec,
file transfer, forwarding, and exposed services are unavailable while
stopped or completed. Starting a retained `Completed` or
`Error/MainProcessFailed` sandbox launches a fresh canonical-main instance and
invalidates SSH sessions from the previous runtime generation. Delete remains
the operation that removes retained state.
The sandbox's SSH host identity survives stop/start and runtime restarts.
API clients can read its public fingerprint from the sandbox resource or
SSH-session response and pin it to the sandbox ID. Recreating a deleted
sandbox, even under the same name, creates a different identity. See the
[sandbox SSH documen...
© NVIDIA · Apache-2.0 · NVIDIA/OpenShell