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 # local Podman with built-in harness
harness apply -f harness.yaml --attach --gateway openshift # Agent config in harness.yaml on OpenShift
harness apply -f harness.yaml --attach --entrypoint opencode # OpenCode

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: reviewerbase_agent: defaultrepo: https://github.com/stackrox/collectortask: "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. What it doesn't provide is the developer workflow layer on top: the config that wires up providers, the deployment abstraction that works the same locally and on a cluster, 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, maintaining separate deployment procedures per environment. The configs diverge, the security posture varies, and nobody catches regressions until something breaks in production.

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. harness-openshell runs the full lifecycle (deploy gateway → register providers → create sandbox → run task → tear down) in CI on every change, across three deployment targets: local Podman, Kind, and OpenShift.

The path from local to automated: a developer runs harness apply --attach for interactive work. When the workflow is ready for CI, they change --attach to --task @skill.md and gateway: local-container to gateway: openshift. Everything else stays the same. No rewriting, no separate deployment tooling. The harness YAML is the artifact — sharable, versionable, forkable.

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 Agent YAML

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: agententrypoint: claudetty: trueproviders:
- profile: github # scoped GITHUB_TOKEN via proxy
- profile: google-vertex-ai # inference routing through gateway
- profile: atlassian # Jira/Confluence via mcp-atlassianenv:
JIRA_URL: # empty = read from host envJIRA_USERNAME:
- profile: google-workspace # Gmail, Calendar, Drive via gws CLIenv:
ANTHROPIC_BASE_URL: https://inference.local # route inference through gateway proxyANTHROPIC_API_KEY: sk-ant-openshell-proxy-managedCLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS: "1"payloads:
- sandbox_path: /sandbox/.claude/CLAUDE.md # agent instructionslocal_path: profiles/images/sandbox-default/CLAUDE.md
- sandbox_path: /sandbox/.claude.json # claude code settingslocal_path: profiles/images/sandbox-default/claude.json
- sandbox_path: /sandbox/.claude/settings.json # permissions and defaultslocal_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: agentname: security-reviewerbase_agent: default # inherits providers, env, payloadsrepo: https://github.com/stackrox/collectortask: "review for memory safety issues"
---
kind: payloadsandbox_path: /sandbox/.claude/CLAUDE.mdcontent: | 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: policynetwork_policies:
github_git:
endpoints:
- host: github.comport: 443rules:
- 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

harness apply -f config.yaml
|
+-> Deploy gateway (Podman container or K8s StatefulSet)
+-> Register providers (credentials from host env)
+-> Upload payloads (CLAUDE.md, MCP config, skills)
+-> Create sandbox (isolated container, deny-by-default network)
+-> Run task (agent executes, outputs results)

OpenShell 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 it once:

openshell gateway add https://127.0.0.1:17670 --local --name 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

Reference

Commands

CommandWhat it does
harness initGenerate a harness.yaml (interactive or --non-interactive)
harness doctorValidate environment (offline + online checks)
harness apply -f FILEDeploy a sandbox from config
harness apply --task TEXTOne-shot headless run
harness apply --task @FILEOne-shot from a skill/playbook file
harness apply --attachInteractive TTY mode
harness apply --dry-runValidate without deploying
harness apply -o yamlOutput resolved config
harness deploy <gateway>Deploy gateway only
harness get agents|providers|gatewaysList resources
harness describe <name>Sandbox details
harness delete <name> [--all]Tear down

Credentials

Each provider discovers credentials from the host. Missing providers are skipped.

ProviderRequired
githubGITHUB_TOKEN env var
google-vertex-aigcloud auth application-default login + ANTHROPIC_VERTEX_PROJECT_ID
atlassianJIRA_API_TOKEN + JIRA_URL + JIRA_USERNAME
google-workspacegws auth login (gws CLI)

Config Files

FilePurpose
profiles/agent-*.yamlAgent configs
profiles/providers/Provider profiles (imported to gateway)
profiles/gateways/*.yamlGateway profiles per target
profiles/images/sandbox-default/Sandbox image defaults (overridable via payloads)

Testing

Tested on macOS (arm64) with Podman. Linux support is expected but not yet validated.

make test# vet + unit tests (5 packages)
make lint # golangci-lint
make test-suite # config parsing (23 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 deploys the gateway, registers all 4 providers, creates sandboxes, verifies exec/env/GWS token resolution/MCP config/Claude inference, tests missing-provider recovery, and tears down.

test-kind creates its own kind cluster, 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 deploy/teardown when iterating.

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

Future Work

  • GitHub Action -- run harness tasks in CI (review PRs, enforce standards, generate reports)
  • Observability -- structured telemetry export (Langfuse, MLflow, OpenTelemetry) for agent tool calls, token usage, and policy decisions
  • Skills integration -- first-class support for community skill packs (e.g., awesome-omni-skills) as task inputs
  • OpenShell plugin -- register the harness as an openshell CLI plugin so openshell harness apply works natively alongside other openshell commands
  • Linux validation -- CI and local testing on Linux (currently macOS-only)

Documentation

DocumentWhat it is
SPEC.mdBehavior spec for the CLI
AGENTS.mdContributor guide
TODO.mdRoadmap and upstream tracking

About

Declarative configuration harness for OpenShell agent sandboxes.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages