Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 84 additions & 131 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,115 +1,84 @@
# harness

Declarative configuration harness for [OpenShell](https://github.com/NVIDIA/OpenShell) AI agent sandboxes.
> **Experimental.** Built on [OpenShell](https://github.com/NVIDIA/OpenShell), which is itself alpha software. Expect breaking changes in both.

```bash
harness apply -f agent.yaml
```

## OpenShell provides the runtime

[OpenShell](https://github.com/NVIDIA/OpenShell) runs AI agents in sandboxed containers with deny-by-default L7 network policy, credential proxy at the network boundary, Landlock filesystem isolation, and inference routing. The harness does not replace any of this.
Declarative workflow layer for OpenShell AI agent sandboxes.

## The harness adds declarative configuration

- **One-file agent definition** -- agent, providers, gateway, policy, and sandbox files in a single YAML
- **Multi-document YAML** -- `kind: agent/provider/gateway/payload/policy` composed in one file
- **Payload files** -- upload configs to sandbox paths without rebuilding the image
- **Headless task mode** -- `--task "do something"` runs the agent and outputs to stdout
- **Multi-target deploy** -- same YAML works on local Podman, kind, and OpenShell
- **Dry-run validation** -- `--dry-run` checks everything before deploying
- **Config inspection** -- `-o yaml` outputs the fully resolved config

## Use OpenShell directly for runtime operations
## Quick Start

```bash
openshell sandbox connect <name> # interactive shell
openshell sandbox exec <name> -- ... # run commands
openshell sandbox logs <name> # view logs
openshell policy get <name> # inspect policy
harness init # generate a config
harness doctor # check your environment
harness apply -f harness.yaml # launch a sandbox
```

The harness handles setup. OpenShell handles the runtime.
### One-shot tasks

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

```bash
# macOS
brew tap nvidia/openshell && brew install openshell && brew services start openshell

# Download the harness binary
curl -L https://github.com/robbycochran/harness-openshell/releases/latest/download/harness_darwin_arm64 -o harness
chmod +x harness
harness apply -f harness.yaml --task "review this codebase for security issues"
harness apply -f harness.yaml --task @skills/cpp-pro/SKILL.md
```

Or build from source: `make cli`
### Coding agent

## Quick Start
Launch an interactive coding session with Claude Code or OpenCode.

```bash
# Set credentials (missing ones are skipped gracefully)
export GITHUB_TOKEN=ghp_...

# Deploy a sandbox with the default agent config
harness apply -f profiles/agent-default.yaml
# Local (Podman)
harness apply -f harness.yaml --attach

# Run a task headlessly (agent outputs to stdout)
harness apply -f profiles/agent-default.yaml --task "review this codebase for security issues"
# On OpenShift
harness apply -f harness.yaml --attach --gateway ocp

# Run a task from a file
harness apply -f profiles/agent-default.yaml --task @tasks/review.md
# OpenCode instead of Claude
harness apply -f harness.yaml --attach --entrypoint opencode
```

# Interactive mode
harness apply -f profiles/agent-default.yaml --attach
## Why this exists

# Validate without deploying
harness apply -f profiles/agent-default.yaml --dry-run
[OpenShell](https://github.com/NVIDIA/OpenShell) is a foundation layer -- sandboxed containers with deny-by-default L7 network policy, credential proxy, Landlock filesystem isolation, and inference routing. It is designed as a strict, secure base that other tooling builds workflows on top of.

# See the fully resolved config
harness apply -f profiles/agent-default.yaml -o yaml
The harness is one such workflow layer. One YAML file defines the agent, providers, payloads, and policy. One command deploys it -- locally via Podman or remotely on Kubernetes.

# Override the entrypoint
harness apply -f profiles/agent-default.yaml --entrypoint opencode
```
OpenShell's upstream direction is toward a [Kubernetes Operator](https://github.com/NVIDIA/OpenShell/issues/1719) 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 -- and covers the local Podman development path that no operator will own.

## The Agent YAML

A single file defines what runs, what credentials it gets, and what files are uploaded to the sandbox.

```yaml
# profiles/agent-default.yaml
name: agent
entrypoint: claude
tty: true

providers:
- profile: github
- profile: vertex-local
- profile: google-vertex-ai
- profile: atlassian
env:
JIRA_URL: ${JIRA_URL}
JIRA_USERNAME: ${JIRA_USERNAME}
- profile: gws

env:
ANTHROPIC_BASE_URL: https://inference.local
ANTHROPIC_API_KEY: sk-ant-openshell-proxy-managed

payloads:
- sandbox_path: /sandbox/.claude/CLAUDE.md
local_path: profiles/images/sandbox-default/CLAUDE.md
- sandbox_path: /sandbox/.claude.json
local_path: profiles/images/sandbox-default/claude.json
- sandbox_path: /sandbox/.mcp.json
local_path: profiles/images/sandbox-default/mcp.json
```

### Multi-Document Harness YAML
### Multi-document YAML

Bundle everything in one file:
Bundle agent, providers, payloads, and policy in one file:

```yaml
---
kind: agent
name: my-agent
name: cpp-reviewer
entrypoint: claude
providers:
- profile: github
Expand All @@ -122,7 +91,7 @@ credentials: [GITHUB_TOKEN]
kind: payload
sandbox_path: /sandbox/.claude/CLAUDE.md
content: |
You are a security review agent.
You are a C++ security review agent.
---
kind: policy
network_policies:
Expand All @@ -131,99 +100,83 @@ network_policies:
- { host: "api.github.com", port: 443 }
```

```bash
harness apply -f harness.yaml
## 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)
```

## Targets
OpenShell provides the runtime isolation. The harness provides the workflow.

For runtime operations, use openshell directly:
```bash
harness apply -f profiles/agent-default.yaml # local Podman
harness apply -f profiles/agent-default.yaml --gateway ocp # deploy to OpenShift
harness apply -f profiles/agent-opencode.yaml # OpenCode agent
harness deploy ocp # deploy gateway only
openshell sandbox connect <name> # interactive shell
openshell sandbox exec <name> -- ... # run commands
openshell sandbox logs <name> # view logs
```

## How It Works

```
harness CLI --> openshell CLI --> Gateway (Podman or K8s)
|-- Provider credentials
|-- L7 network policy
|-- inference.local proxy
+-- Sandbox container
|-- claude / opencode
|-- gh, mcp-atlassian, gws
+-- placeholder tokens
```
## Install

The harness orchestrates three OpenShell components:
```bash
# macOS
brew tap nvidia/openshell && brew install openshell && brew services start openshell

- **Gateway** -- credential proxy and L7 network policy engine. Runs as Podman container (local) or K8s StatefulSet (remote).
- **Providers** -- credential registrations. Provider profiles in `profiles/providers/` are imported to the gateway. Missing credentials are skipped.
- **Sandbox** -- isolated container running the agent entrypoint. Credentials are proxy-managed placeholder tokens. Network egress is deny-by-default at L7.
# Download the harness binary
curl -L https://github.com/robbycochran/harness-openshell/releases/latest/download/harness_darwin_arm64 -o harness
chmod +x harness
```

See the [OpenShell docs](https://github.com/NVIDIA/OpenShell) for the full security model.
Or build from source: `make cli`

## Reference

### Commands

```
harness apply -f FILE [--task TEXT|@FILE] [--entrypoint NAME] [--gateway NAME] [--attach] [--dry-run] [-o yaml|json]
Deploy a sandboxed agent from a config file.
-f is required -- always specify which config to apply.
--task runs the agent headlessly with a task (inline text or @filepath).
--entrypoint overrides the agent entrypoint (claude, opencode, bash).
--attach enables interactive TTY mode.
--dry-run validates without deploying.
-o yaml outputs the fully resolved config.

harness deploy [local|ocp|kind]
Deploy or verify the gateway for a target.

harness get agents|providers|gateways [-o table|json|yaml]
List resources with consistent structured output.

harness describe <name> [-o table|json|yaml]
Detailed status for a specific sandbox.

harness delete <name> [--all] [--providers] [--k8s]
Delete sandboxes or other resources.
```

For runtime operations, use openshell directly:
```
openshell sandbox connect [NAME]
openshell sandbox logs [NAME] [--tail]
openshell sandbox exec [NAME] -- ...
```

### Config Files

| File | Purpose |
|------|---------|
| `profiles/agent-*.yaml` | Agent config: image, entrypoint, providers, env, payloads, task |
| `profiles/providers/` | OpenShell provider profiles (imported to gateway on registration) |
| `profiles/gateways/*.yaml` | Gateway profiles: `local.yaml`, `kind.yaml`, `ocp.yaml` |
| `profiles/images/sandbox-default/` | Sandbox image defaults (overridable via payloads) |
| 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` | Validate without deploying |
| `harness apply -o yaml` | Output resolved config |
| `harness deploy [local\|ocp\|kind]` | Deploy gateway only |
| `harness get agents\|providers\|gateways` | List resources |
| `harness describe <name>` | Sandbox details |
| `harness delete <name> [--all]` | Tear down |

### Credentials

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

| Provider | Required |
|----------|----------|
| `github` | `GITHUB_TOKEN` env var |
| `vertex-local` | `gcloud auth application-default login` + `ANTHROPIC_VERTEX_PROJECT_ID` + `CLOUD_ML_REGION` |
| `google-vertex-ai` | `gcloud auth application-default login` + `ANTHROPIC_VERTEX_PROJECT_ID` |
| `atlassian` | `JIRA_API_TOKEN` + `JIRA_URL` + `JIRA_USERNAME` |
| `gws` | `gws auth login` (OAuth via [gws CLI](https://github.com/googleworkspace/cli)) |
| `google-workspace` | `gws auth login` ([gws CLI](https://github.com/googleworkspace/cli)) |

### Config Files

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

## Documentation Map
## Documentation

| Document | What it is |
|----------|------------|
| [SPEC.md](SPEC.md) | Authoritative behavior spec for the CLI |
| [AGENTS.md](AGENTS.md) | Contributor guide: coding principles, upstream conventions, validation |
| [TODO.md](TODO.md) | Roadmap and known gaps |
| [docs/archive/](docs/archive/README.md) | Historical design docs |
| [SPEC.md](SPEC.md) | Behavior spec for the CLI |
| [AGENTS.md](AGENTS.md) | Contributor guide |
| [TODO.md](TODO.md) | Roadmap and upstream tracking |
4 changes: 2 additions & 2 deletions SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,12 @@ tty: true

providers:
- profile: github
- profile: vertex-local
- profile: google-vertex-ai
- profile: atlassian
env:
JIRA_URL: ${JIRA_URL}
JIRA_USERNAME: ${JIRA_USERNAME}
- profile: gws
- profile: google-workspace

env:
ANTHROPIC_BASE_URL: https://inference.local
Expand Down
38 changes: 23 additions & 15 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

## Next up

### `harness init`
- [ ] Generate a default `harness.yaml` in the current directory
- [ ] Detect available credentials and suggest providers
- [ ] Print next steps ("run `harness apply -f harness.yaml`")
- [ ] Highest-impact missing feature for standalone distribution

### `harness doctor`
- [ ] Check openshell installed and >= 0.0.59
- [ ] Check podman/docker running
- [ ] Check gateway reachable
- [ ] Check credentials available (GITHUB_TOKEN, ADC, JIRA, GWS)
- [ ] Actionable error messages ("install with: brew tap nvidia/openshell...")
### `harness init` [DONE]
- [x] Generate a harness.yaml with interactive prompts (entrypoint, providers, gateway)
- [x] Discover providers from `openshell provider list-profiles`
- [x] Print next steps ("run `harness doctor` then `harness apply`")
- [x] `--non-interactive`, `--force`, `--output` flags

### `harness doctor` [DONE]
- [x] Check openshell installed and version
- [x] Check target-specific deps (podman/docker, kubectl, kind, kubeconfig)
- [x] Check provider credentials via `openshell provider profile export`
- [x] Online phase: check provider registration if gateway reachable
- [x] `-o table|json|yaml` output

### registerProviders should filter by agent's provider list
- `registerProviders()` in `cmd/providers.go` registers all providers regardless
Expand Down Expand Up @@ -62,12 +62,20 @@
- Prerequisite: proto files stabilize (OpenShell is alpha)

### Upstream issues to track
- #1719 -- K8s Operator design (affects provider CRDs)
- #1719 -- K8s Operator design (providers as CRDs, gateway narrows to data-plane)
- #1851 -- Plugin system (affects binary naming)
- #1886 -- Declarative provider config in gateway.toml
- #1886 -- Declarative provider config in gateway.toml (core team rejected; redirected to #1719)
- #1520 -- Sandbox specs / apply -f (stale, no maintainer engagement)
- #1814 -- Named sandbox templates (no comments, blocked on #863)
- #1922 -- Portable sandbox log collection
- #1933 -- Centralized audit/event log

Upstream direction signal (as of 2026-06): the gateway stays a strict foundation
layer. Provider lifecycle and sandbox declaration are moving toward the operator/CRD
model for K8s. johntmyers mentioned hooks/middleware for API calls coming soon.
The harness's provider registration and multi-document YAML have no upstream
replacement on the current roadmap.

## Observability & Tracing

Langfuse hooks plugin working. MLflow spiked. SigNoz identified as strongest
Expand All @@ -76,4 +84,4 @@ OTel backend. Integration deferred until `init`/`doctor` ship.
## Release

- [x] CHANGELOG.md + LICENSE (Apache 2.0)
- [ ] `harness init` for standalone binary distribution
- [x] `harness init` for standalone binary distribution
Loading
Loading