Behavior specification for the OpenShell Harness CLI.
The harness deploys and manages AI agent sandboxes on three targets:
- local-container -- Podman containers via a local OpenShell gateway
- helm -- Kubernetes pods via a k8s cluster (NodePort access)
- openshift -- Kubernetes pods via an OpenShift-hosted OpenShell gateway (Route access)
Each sandbox is an isolated container running an agent entrypoint (Claude Code or OpenCode), with credential providers, network policies, and a rendered payload (run.sh, task.md).
Requires OpenShell v0.0.59+.
Agent configs live in profiles/agent-<name>.yaml. Each declares the sandbox image, entrypoint, providers, and environment:
name: agent
entrypoint: claude # or: opencode
tty: true
providers:
- profile: github
- profile: google-vertex-ai
- profile: atlassian
env:
JIRA_URL:
JIRA_USERNAME:
- profile: google-workspace
env:
ANTHROPIC_BASE_URL: https://inference.localFields:
name(required) -- sandbox name, used foropenshell sandbox connectbase_agent-- name of a base agent config to inherit from (e.g.,defaultresolvesagent-default.yaml). Providers, env, and payloads are merged additively; scalar fields (entrypoint, gateway, repo, task, image, policy) from the overlay win when non-empty.image-- container image for the sandbox (default: version-matched from ghcr.io, override withHARNESS_OS_IMAGEenv)entrypoint-- command to run (default:claude). Supportsclaude,opencode,bash, or any binary on PATH.tty-- enable TTY (default: true)repo-- git URL to clone outside the sandbox and upload to/sandbox/<repo-name>. Shallow clone (--depth 1) with submodules. Git credentials never enter the sandbox unless needed.repo_ref-- branch, tag, or ref to clone (default: HEAD). Passed as--branchto git clone.task-- path to a task.md file, passed to entrypoint via-p "$(cat task.md)"providers-- list of provider profile referencesproviders[].profile-- OpenShell provider profile nameproviders[].env-- non-secret env vars for this provider (resolved viaos.ExpandEnv; empty values read from host env; injected via--envon sandbox create)env-- additional environment variables injected via--envon sandbox create (empty values read from host env)include-- extra files to include in the payloadpolicy-- path to a network policy YAMLgateway-- target gateway name (overrides active gateway)
Provider profiles live in profiles/providers/. These are imported to the gateway during provider registration.
Agent configs support multi-document YAML (--- separated) where provider, gateway, and policy definitions are co-located in one file:
---
kind: agent
name: my-agent
entrypoint: claude
providers:
- profile: github
---
kind: provider
name: github
type: github
credentials: [GITHUB_TOKEN]
---
kind: gateway
name: local-container
type: localDocuments are dispatched by kind field. No kind field = agent (backwards compatible). Definitions in the harness file take priority over the profiles/ tree.
harness apply [-f FILE] [--agent NAME] [--gateway NAME] [--gateway-profile FILE] [--name SANDBOX] [--attach] [--provider-refresh] [--dry-run] [-o yaml|json]
Primary command. Resolves an agent config, deploys the gateway and providers, creates a sandbox.
- Parse agent config -- resolve
agent-<name>.yamlfrom harness directory (default:default).-foverrides with a direct file path. Falls back to embeddedagent-basic.yamlwhenagent-default.yamlis not found on disk. - Check output mode -- if
-o yamlor-o json, render the fully resolved config and exit. No gateway interaction needed. - Check version -- warn if openshell CLI is below v0.0.59.
- Resolve gateway --
--gatewayselects a profile by name;--gateway-profileloads from a file path. Default:local-container.OPENSHELL_GATEWAYenv var is used as fallback. - Dry-run check -- if
--dry-run, validate each step (gateway reachable, providers resolvable, env vars resolved, image available) and exit with pass/fail report. - Ensure gateway -- deploy if needed (local: Podman, remote: Helm to K8s/OCP).
- Ensure providers -- auto-register missing providers. Three registration flows:
- Standard (
--from-existing): GitHub, Atlassian -- OpenShell discovers credentials from local env. - ADC (
--from-gcloud-adc): Vertex AI -- reads ADC file, configures inference routing. - Custom: GWS -- multi-step OAuth refresh flow.
- Standard (
- Render payload --
run.sh(entrypoint wrapper with PATH setup, entrypoint validation,-ptask),task.md(if set). - Create sandbox --
openshell sandbox createwith--env(env vars),--upload(payload), and startup command. Retry up to 5 times.
Default is non-interactive (headless). Use --attach for TTY mode.
--provider-refresh deletes and recreates all providers.
List resources. Wraps openshell list commands with consistent structured output across resource types. -o table is the default. Credential values are never included in structured output.
| Subcommand | Aliases | What it lists |
|---|---|---|
get agents |
sandboxes, sandbox |
Running sandboxes (name, phase) |
get providers |
provider |
Registered providers (name only, no credentials) |
get gateways |
gateway, gw |
Gateways (name, endpoint, active) |
These are convenience wrappers. For full details, use openshell sandbox list, openshell provider list, etc. directly.
Show detailed status for a specific sandbox: phase, active gateway, and registered providers.
Delete sandboxes by name, or use flags for bulk operations. --all deletes sandboxes, providers, and k8s resources. Reuses the same teardown functions as the old teardown command.
Deploy or verify the gateway for a target. Reads profiles/gateways/<target>.yaml.
These commands still work but will be removed in a future release:
| Old command | Replacement | Notes |
|---|---|---|
harness teardown |
harness delete |
Same flags: --sandboxes, --providers, --k8s |
harness status |
harness get agents |
| File | Purpose |
|---|---|
profiles/agent-*.yaml |
Agent config: image, entrypoint, providers, env, task |
profiles/providers/ |
OpenShell provider profile YAMLs |
profiles/gateways/*.yaml |
Gateway profiles: deployment target config with inline Helm values |
profiles/images/sandbox-default/Dockerfile |
Sandbox image: OpenShell base + MCP servers + CLI tools |
profiles/images/sandbox-default/CLAUDE.md |
Claude Code project instructions for sandbox |
profiles/images/sandbox-default/claude.json |
Claude Code settings |
profiles/images/sandbox-default/mcp.json |
MCP server config for Claude agent |
profiles/images/sandbox-default/opencode.json |
MCP server config for OpenCode agent |
profiles/images/sandbox-default/policy.yaml |
Network egress rules applied to sandboxes |
profiles/images/sandbox-default/settings.json |
Claude Code settings overlay |
All images are published to quay.io/rcochran/openshell. CI never publishes floating tags (:latest, :sandbox); the bare :sandbox fallback below exists only for local go build binaries without version ldflags.
| Trigger | Sandbox |
|---|---|
Release v0.1.2 |
:sandbox-v0.1.2 |
| Any push/PR | :sandbox-<sha> |
The CLI resolves images from its embedded version (set via -ldflags at build time):
v0.1.2->:sandbox-v0.1.2(tagged release)v0.1.2-5-gabc1234->:sandbox-v0.1.2-5-gabc1234(dev build, matchesmake dev-sandbox)dev->:sandbox(barego buildwithout ldflags)
HARNESS_OS_IMAGE env var overrides the version-based resolution.
Harness-specific variables use the HARNESS_OS_ prefix. OpenShell runtime variables use OPENSHELL_.
| Variable | Purpose |
|---|---|
HARNESS_OS_DIR |
Override harness directory detection |
HARNESS_OS_IMAGE |
Override sandbox image (dev/CI builds) |
HARNESS_OS_PULL_SECRET |
Image pull secret name passed to Helm install |
HARNESS_OS_SANDBOX_PULL_SECRET |
Sandbox image pull secret name passed to Helm install |
OPENSHELL_CLI |
Override openshell binary path |
OPENSHELL_GATEWAY |
Override gateway name (used by apply, plugin-compatible) |
OPENSHELL_NAMESPACE |
Override K8s namespace (default: openshell) |
OPENSHELL_MODEL |
Inference model for provider registration (default: claude-sonnet-4-6) |
OPENSHELL_CHART_VERSION |
Override Helm chart version (beats gateway.yaml) |
KUBECONFIG |
K8s cluster config for remote targets |
The harness renders agent config into a self-contained payload uploaded to /sandbox/.config/openshell/:
openshell/
run.sh -- validates entrypoint, execs it (with -p task if set)
task.md -- task file with envsubst applied (if task: is set)
bin/ -- wrapper scripts
Environment variables are injected directly via --env KEY=VALUE flags on openshell sandbox create -- no file upload needed for env vars. run.sh is the entrypoint for interactive mode.