Skip to content

Repository files navigation

harness

Experimental. Built on OpenShell, which is itself alpha software. Expect breaking changes in both.

Declarative workflow layer for OpenShell AI agent sandboxes.

Quick Start

harness init                        # generate a config
harness doctor                      # check your environment
harness apply -f harness.yaml       # launch a sandbox

Coding agent

Launch an interactive coding session with Claude Code or OpenCode.

harness apply --attach                                        # built-in default agent
harness apply -f harness.yaml --attach                        # agent config in harness.yaml
harness apply -f harness.yaml --attach --entrypoint opencode  # OpenCode

For a v1alpha1 workflow, harness apply uses spec.target, --gateway and --workspace (flag, environment, then config precedence). Legacy configs retain the selected-gateway fallback. Provisioning the gateway is OpenShell's or HyperShell's job, not the harness's (see Install).

One-shot tasks

Run a task headlessly -- the agent executes in a sandbox and outputs results.

harness apply -f harness.yaml --task "review this codebase for security issues"
harness apply -f harness.yaml --task @skills/cpp-pro/SKILL.md

Clone a repo into the sandbox

Use base_agent to inherit providers and inference routing from an existing config. The repo field clones the repository outside the sandbox and uploads it -- OpenShell sandboxes have no host mounts by design.

name: reviewer
base_agent: default
repo: https://github.com/stackrox/collector
task: "identify the highest-priority C++ remediation"
harness apply -f reviewer.yaml

To get results out: --task mode outputs to stdout, openshell sandbox exec pulls files, or attach a github provider so the agent can push directly via the scoped proxy token.

Why this exists

OpenShell provides a strict, secure sandbox runtime — deny-by-default L7 network policy, credential proxying, Landlock filesystem isolation, and inference routing. It also provisions the gateway itself (the local installer, or helm install openshell on a cluster). What it doesn't provide is the developer workflow layer on top: the config that wires up providers, the declarative reconciliation that makes a gateway match your intent, or the CI harness that catches breakage before developers hit it.

Without a shared harness layer, every team building on OpenShell independently solves the same problems — writing shell scripts to register providers, hand-rolling container images, re-deriving inference routing. The configs diverge, the security posture varies, and nobody catches regressions until something breaks in production.

The design boundary: managing a gateway is OpenShell's problem; the harness is a declarative setup/run layer with zero compute-backend opinion. It never provisions or tears down a gateway — it declares providers, inference, and policy against one OpenShell already stood up, and runs agents in it. The workflow remains portable because its target can be overridden by standard gateway and workspace flags or environment variables.

The core design constraint: if the developer harness isn't running and live-tested in CI, the developer experience can't be maintained. OpenShell, agent CLIs, and provider APIs all change frequently — often multiple times per week. A harness that works today and isn't continuously validated will silently break. CI exercises the workflow against local and Kind gateways on Linux. OpenShift remains a manually credentialed integration target.

The path from local to automated: a developer runs harness apply --attach for interactive work, then checks agent arguments and payloads into the v1alpha1 workflow for headless CI. Legacy configs can continue to use --task @skill.md during migration. The goal is for the same versioned workflow declaration to be sharable, forkable, and executable in both environments.

Current schema transition: harness plan and harness apply now share strict parsing, environment resolution, target resolution, and action decisions for harness.openshell.dev/v1alpha1. Legacy agent files remain accepted by apply through a compatibility path; use harness migrate to produce the canonical format. New fields are added only to v1alpha1.

OpenShell's upstream direction is toward a Kubernetes Operator where providers and sandboxes become CRDs and the gateway narrows to data-plane only. The harness explores what the workflow layer looks like above that with a developer mindset from local machine to cluster.

The v1alpha1 workflow

The canonical workflow is accepted by both plan and apply:

apiVersion: harness.openshell.dev/v1alpha1
kind: Harness
metadata:
  name: security-review
spec:
  target:
    gateway: acs
    workspace: stackrox
  providers:
    - name: github-read
      management: referenced
  sandbox:
    image: quay.io/example/security-reviewer:v1
    providers: [github-read]
    keep: false
    tty: false
  agent:
    type: claude
    args: [--print, "Review the repository for security defects"]
  source:
    repo: https://github.com/stackrox/stackrox
    ref: main
    destination: /sandbox/stackrox

plan is read-only and may render desired state while the gateway is offline. apply requires the effective gateway to be reachable, verifies referenced providers before sandbox creation, and disables OpenShell provider auto-discovery. Managed providers may be updated or explicitly adopted, but apply does not create credentialed providers; platform bootstrap owns their creation. Relative payload and policy paths resolve from the workflow file's directory.

Remote-image, non-interactive workflows use the OpenShell SDK for sandbox creation, readiness, execution, and cleanup. Source or payload uploads, local image builds, policy files, and interactive TTYs currently use the OpenShell CLI because those paths do not yet have complete SDK-native implementations.

The Agent YAML

The legacy Agent YAML remains available during migration. It does not use the canonical v1alpha1 execution path below.

A single file defines the entrypoint, credential providers, inference routing, environment, and files uploaded to the sandbox. This is the default config (profiles/agent-default.yaml):

name: agent
entrypoint: claude
tty: true

providers:
  - profile: github                               # scoped GITHUB_TOKEN via proxy
  - profile: google-vertex-ai                     # inference routing through gateway
  - profile: atlassian                            # Jira/Confluence via mcp-atlassian
    env:
      JIRA_URL:                                   # empty = read from host env
      JIRA_USERNAME:
  - profile: google-workspace                     # Gmail, Calendar, Drive via gws CLI

env:
  ANTHROPIC_BASE_URL: https://inference.local     # route inference through gateway proxy
  ANTHROPIC_API_KEY: sk-ant-openshell-proxy-managed
  CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1"

payloads:
  - sandbox_path: /sandbox/.claude/CLAUDE.md      # agent instructions
    local_path: profiles/images/sandbox-default/CLAUDE.md
  - sandbox_path: /sandbox/.claude.json           # claude code settings
    local_path: profiles/images/sandbox-default/claude.json
  - sandbox_path: /sandbox/.claude/settings.json  # permissions and defaults
    local_path: profiles/images/sandbox-default/settings.json
  - sandbox_path: /sandbox/.mcp.json              # MCP server config (jira, confluence)
    local_path: profiles/images/sandbox-default/mcp.json

Credentials never enter the sandbox -- the gateway proxy resolves placeholder tokens at the network boundary. Each provider also contributes its own L7 network policy endpoints and binary allowlists.

Use harness apply -o yaml to see the fully resolved config -- providers expand to show credential definitions, endpoint policies, scopes, and refresh strategies.

Multi-document YAML

Bundle agent, providers, payloads, and policy in one self-contained file. Use base_agent to inherit from an existing config:

---
kind: agent
name: security-reviewer
base_agent: default                               # inherits providers, env, payloads
repo: https://github.com/stackrox/collector
task: "review for memory safety issues"
---
kind: payload
sandbox_path: /sandbox/.claude/CLAUDE.md
content: |
  You are a C++ security review agent specializing in RAII,
  move semantics, and concurrency safety. Focus on the
  highest-priority remediation and explain the fix.
---
kind: policy
network_policies:
  github_git:
    endpoints:
      - host: github.com
        port: 443
        rules:
          - allow: { method: GET, path: "/**/info/refs*" }
          - allow: { method: POST, path: "/**/git-upload-pack" }
    binaries:
      - { path: /usr/bin/git }

This inherits all four providers and inference routing from agent-default.yaml, adds a custom CLAUDE.md as the agent's instructions, and defines an L7 policy that allows git clone but blocks git push at the HTTP method level.

How It Works

(OpenShell has already provisioned the gateway; you selected it)
harness apply -f config.yaml
    |
    +-> Verify/reconcile declared providers and inference
    +-> Upload payloads (CLAUDE.md, MCP config, skills)
    +-> Create sandbox (isolated container, deny-by-default network)
    +-> Run task (agent executes, outputs results)

OpenShell provisions the gateway and provides the runtime isolation. The harness provides the workflow.

For runtime operations and policy management, use openshell directly:

openshell sandbox connect <name>     # interactive shell
openshell sandbox exec <name> -- ... # run commands
openshell sandbox logs <name>        # view logs
openshell policy get <name>          # inspect active policy
openshell term                       # interactive policy terminal

openshell term provides a live view of policy decisions -- which requests are allowed, denied, or pending review. This is how you audit and tune the deny-by-default L7 network policy while an agent is running.

Install

# OpenShell CLI + local gateway, pinned to the version this repo targets
# (.openshell-version). Installs the exact release CI uses and starts the
# managed gateway service (Homebrew/launchd on macOS, systemd on Linux).
make openshell

# Download the harness binary
curl -L https://github.com/stackrox/harness-openshell/releases/latest/download/harness_darwin_arm64 -o harness
chmod +x harness

Install a bare brew install openshell off the tap and you get whatever version the formula defaults to — usually behind. make openshell runs the upstream install.sh at the pinned version instead, so local matches CI exactly.

The installer starts the gateway service; register and select it once:

openshell gateway add https://127.0.0.1:17670 --local --name openshell
openshell gateway select openshell

If you need to restart the service later: brew services restart openshell (macOS) or systemctl --user restart openshell-gateway (Linux).

Or build the harness from source: make cli

On a cluster

Provisioning a cluster gateway is OpenShell's job too — the harness has no deploy command. Install the chart, then register and select the gateway:

helm install openshell oci://ghcr.io/nvidia/openshell/helm-chart
openshell gateway add https://<gateway-endpoint> --name my-cluster
openshell gateway select my-cluster
harness apply -f harness.yaml            # same YAML, cluster gateway

Tear the gateway down with helm uninstall openshell and openshell gateway remove my-cluster. The harness delete command removes sandboxes; add --providers (or --all) to remove providers too. It never removes the gateway.

Migration: harness deploy, harness teardown, harness status, and delete --k8s are removed. Provision the gateway with OpenShell (the openshell installer or helm install openshell); the harness declares providers/inference/policy and runs agents against it.

Reference

Commands

Command What it does
harness init Generate a harness.yaml (interactive or --non-interactive)
harness doctor Validate environment (offline + online checks)
harness apply -f FILE Deploy a sandbox from config
harness apply --task TEXT One-shot headless run
harness apply --task @FILE One-shot from a skill/playbook file
harness apply --attach Interactive TTY mode
harness apply --dry-run Render the v1alpha1 action plan without mutating
harness apply -o yaml Output resolved config
harness get agents|providers|gateways List resources
harness describe <name> Sandbox details
harness delete <name> [--all] Tear down
harness plan -f FILE Read-only reconciliation plan (mutates nothing)
harness migrate -f FILE Convert a legacy v1 config to v1alpha1

Credentials

The legacy compatibility path discovers provider credentials from the host and may skip missing providers. Canonical v1alpha1 apply is strict: referenced providers must already exist, and credentialed provider creation is a separate platform/bootstrap responsibility.

Provider Required
github GITHUB_TOKEN env var
google-vertex-ai gcloud auth application-default login + ANTHROPIC_VERTEX_PROJECT_ID
atlassian JIRA_API_TOKEN + JIRA_URL + JIRA_USERNAME
google-workspace gws auth login (gws CLI)

Config Files

File Purpose
profiles/agent-*.yaml Agent configs
profiles/providers/ Provider profiles (imported to gateway)
profiles/images/sandbox-default/ Sandbox image defaults (overridable via payloads)

Testing

Developer testing primarily uses macOS (arm64) with Podman. GitHub Actions runs unit, local-gateway, and Kind integration coverage on Linux. OpenShift integration is available as a manually credentialed target.

make test             # vet + unit tests (16 packages)
make lint             # golangci-lint
make test-suite       # config parsing (32 tests, no gateway needed)
make test-local       # full e2e on local Podman (22 tests)
make test-kind        # self-contained kind cluster lifecycle
make test-remote      # full e2e on OCP (needs KUBECONFIG)

test-local is the primary validation target. It provisions a gateway via the OpenShell installer, registers all 4 providers, creates sandboxes, verifies exec/env/GWS token resolution/MCP config/Claude inference, tests missing-provider recovery, and tears down the sandboxes and providers it created (the gateway is OpenShell's to remove).

test-kind creates its own kind cluster, helm installs OpenShell, builds and loads the sandbox image, runs the full flow, and deletes the cluster on exit. Use KEEP=1 to keep the cluster for debugging.

test-remote requires KUBECONFIG pointing at an OCP cluster and pushes the image automatically. Use --reuse-gateway to skip gateway provisioning/teardown when iterating.

Each integration target builds (and pushes, for remote) the sandbox image automatically.

Documentation

Document What it is
AGENTS.md Contributor guide
docs/ci.md HyperShell CI bootstrap and repository contract
docs/archive/ Historical design context; not current behavior

About

Declarative configuration harness for OpenShell agent sandboxes.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages