Cloud and BYOC for Orca Agent Engine are in Private Preview — request an invite
Docs

Run an agent with ork local

Start Orca Agent Engine with ork local and use its API, CLI, and SDKs against a local registry.

ork local starts Orca Agent Engine on one machine with Docker Compose. It runs registry, harness, Postgres, and RustFS, and creates a Workspace API key for local clients. This tutorial uses that key to create an agent, run a session, and test a request guardrail.

The CLI v0.5.0 default Compose images predate Agent Engine v0.5.1. Without the release image overrides below, the local registry rejects codex_sdk and pi_sdk at agent creation and does not expose the harness catalog.

Start the stack

Install Docker with Compose v2 and jq. A provider-backed Claude session also needs an Anthropic API key. Install ork with Homebrew on macOS or Linux:

Install ork
brew install orca-ae/tap/ork
ork --version

For Windows or a system without Homebrew, use the v0.5.0 release archive or the Go installation.

Set the provider key in the shell before starting the stack. Harness reads it at startup; restart after changing it.

Start the local engine
export ANTHROPIC_API_KEY="<your-anthropic-api-key>"
export ORCA_LOCAL_REGISTRY_IMAGE="docker.io/streamnative/orca-registry-service-ts:0.5.1"
export ORCA_LOCAL_HARNESS_IMAGE="docker.io/streamnative/orca-harness-server:0.5.1"
export ORCA_LOCAL_GATEWAY_IMAGE="docker.io/streamnative/orca-ai-gateway:0.4.3"
LOCAL_DIR="$HOME/.ork-local"
ork local --data-dir "$LOCAL_DIR" start
ork local --data-dir "$LOCAL_DIR" status

export ORCA_REGISTRY_URL="http://127.0.0.1:8080"
export ORCA_BASE_URL="$ORCA_REGISTRY_URL"
export ORCA_API_KEY="$(cat "$LOCAL_DIR/secrets/workspace-api-key")"
ork api-groups

The Registry and Harness images are the public Docker Hub builds for Agent Engine v0.5.1. ork api-groups lists runtime.runorca.ai, policy.runorca.ai, and pricing.runorca.ai. Inspect the models available to each harness with:

List the harness catalog
curl -fsS "$ORCA_REGISTRY_URL/apis/runtime.runorca.ai/v1/harnesses" \
  -H "x-api-key: $ORCA_API_KEY" | jq -r '.data[].id'

Keep the key file private. The CLI and Go SDK send ORCA_API_KEY as x-api-key; the TypeScript and Python SDKs send their apiKey or api_key setting as a Bearer token by default. Configure those two SDKs with an explicit x-api-key header below. Both base URLs are the host root, without /v1.

The stack uses an unisolated in-memory sandbox. Run only agents and code you trust. By default, Harness calls the model provider directly. ork local --data-dir "$LOCAL_DIR" start --with-gateway starts Orca AI Gateway from the pinned image for model and MCP egress; see Gateway integration.

Create an environment and a Claude agent

Install the TypeScript SDK with npm install @runorca/orca-sdk@0.2.3, the Python SDK with pip install runorca==0.3.0, or the Go SDK with go get github.com/orca-ae/orca-sdk-go@v0.4.0 if you use those examples. Each example creates the same two resources; choose one interface and keep its returned IDs for the next step.

environment=$(ork agent environments create --name "local-tutorial" -o json)
ENV_ID=$(jq -r '.id' <<< "$environment")
agent=$(ork agent create --name "local-claude" --model claude-sonnet-4-6 \
  --metadata harness=claude_agent_sdk -o json)
AGENT_ID=$(jq -r '.id' <<< "$agent")

The Python SDK uses the same Workspace key:

Python SDK
import os
from orca import Orca

client = Orca(
    base_url=os.environ["ORCA_REGISTRY_URL"],
    api_key=None,
    default_headers={"x-api-key": os.environ["ORCA_API_KEY"]},
)
environment = client.environments.create(name="local-tutorial")
agent = client.agents.create(
    name="local-claude",
    model="claude-sonnet-4-6",
    metadata={"harness": "claude_agent_sdk"},
)

The Go SDK reads ORCA_BASE_URL and sends ORCA_API_KEY as x-api-key. Run the tested Go example to create the same resources.

If your shell uses a proxy, clear its proxy variables before running the local Python SDK examples and exempt loopback traffic. This avoids a SOCKS transport error or a proxy 502 from the local registry:

Use the Python SDK against loopback
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY http_proxy https_proxy all_proxy
export NO_PROXY="127.0.0.1,localhost"
export no_proxy="$NO_PROXY"

Run a session

Send a message, then inspect events. The response needs the Anthropic key supplied when you started Harness.

session=$(ork agent sessions create --agent "$AGENT_ID" --environment-id "$ENV_ID" -o json)
SESSION_ID=$(jq -r '.id' <<< "$session")
ork agent sessions events send message --session "$SESSION_ID" --text "Say hello." -o json
ork agent sessions events list --session "$SESSION_ID" -o json
Python SDK
session = client.sessions.create(agent=agent.id, environment_id=environment.id)
client.sessions.events.send(
    session.id,
    events=[{"type": "user.message", "content": [{"type": "text", "text": "Say hello."}]}],
)
for event in client.sessions.events.list(session.id):
    print(event.type)

Session execution is asynchronous. If the first event list contains only user.message, list again or stream the events until session.status_idle appears.

Try a request guardrail

The policy.runorca.ai/v1 extension is available in the tested local stack. Create an explicit rule that denies every request. This is a diagnostic rule: attach it only to a disposable agent, then send a message to see session.error with a guardrail denial. The rule rejects before a provider call, so this check needs no model key.

ork guardrails list-types -o json
guardrail=$(ork guardrails create --config-json \
  '{"name":"local-deny-request","scope":"explicit","phases":["request"],"rule":{"kind":"expression","expression":"false","on_false":"deny"}}' -o json)
GUARDRAIL_ID=$(jq -r '.id' <<< "$guardrail")
Python SDK
types = client.guardrails.list_types()
print(len(types.data))
guardrail = client.guardrails.create(
    name="local-deny-request",
    scope="explicit",
    phases=["request"],
    rule={"kind": "expression", "expression": "false", "on_false": "deny"},
)

Attach the rule to a new agent. The registry stores guardrail_ids without extra headers but returns the field only to requests that send orca-beta, so these examples send it. None of the SDKs adds the header: pass it in the TypeScript request options, in Python's extra_headers, or with Go's option.WithHeader. The CLI does not expose guardrail_ids on agent create, so use the API or an SDK for this step. See the Go guardrail example for its typed request.

const guardedAgent = await orca.agents.create(
  {
    name: 'local-guarded-claude',
    model: 'claude-sonnet-4-6',
    metadata: { harness: 'claude_agent_sdk' },
    guardrail_ids: [guardrail.id],
  },
  { headers: { 'orca-beta': 'guardrails' } },
);
Python SDK
guarded_agent = client.agents.create(
    name="local-guarded-claude",
    model="claude-sonnet-4-6",
    metadata={"harness": "claude_agent_sdk"},
    guardrail_ids=[guardrail.id],
    extra_headers={"orca-beta": "guardrails"},
)

Create a session with the guarded agent and send a message:

const guardedSession = await orca.sessions.create({
  agent: guardedAgent.id,
  environment_id: environment.id,
});
await orca.sessions.events.send(guardedSession.id, {
  events: [{ type: 'user.message', content: [{ type: 'text', text: 'Hello.' }] }],
});
console.log((await orca.sessions.events.list(guardedSession.id)).data);
Python SDK
guarded_session = client.sessions.create(agent=guarded_agent.id, environment_id=environment.id)
client.sessions.events.send(
    guarded_session.id,
    events=[{"type": "user.message", "content": [{"type": "text", "text": "Hello."}]}],
)
for event in client.sessions.events.list(guarded_session.id):
    if event.type == "session.error":
        print(event)

Execution is asynchronous. Repeat the final list call if the event has not appeared yet. A denial produces session.error with The request was denied by a managed-agent guardrail. See Guardrails for scopes, builtins, phases, and enforcement limits.

Select another harness

The agent's metadata.harness selects its loop. Agent Engine v0.5.1 accepts all three configurations in this stack. The local results below were verified with the v0.5.0 images; v0.5.1 changes neither agent creation nor request guardrails:

HarnessModel exampleProvider key for direct egressLocal resultGuide
claude_agent_sdkclaude-sonnet-4-6ANTHROPIC_API_KEYAgent creation and request denial verifiedClaude Agent SDK
codex_sdkgpt-5.4OPENAI_API_KEYAgent creation and request denial verifiedCodex SDK
pi_sdk{ "provider": "anthropic", "id": "claude-sonnet-4-6" }ANTHROPIC_API_KEYAgent creation and request denial verifiedPi SDK

Set the matching provider key before starting ork local. For Codex, export OPENAI_API_KEY in the shell and run ork local --data-dir "$LOCAL_DIR" start again to recreate Harness. Pi with the Anthropic model uses the ANTHROPIC_API_KEY set at the start of this tutorial. The request denials above used diagnostic rules and did not call a model, but Codex and Pi still required a configured provider key to initialize. Use the same session flow after creating either agent:

Create a Codex agent
agent=$(ork agent create --name "local-codex" --model gpt-5.4 \
  --metadata harness=codex_sdk -o json)
AGENT_ID=$(jq -r '.id' <<< "$agent")
Create a Pi agent
agent=$(ork agent create --name "local-pi" \
  --model-json '{"provider":"anthropic","id":"claude-sonnet-4-6"}' \
  --metadata harness=pi_sdk -o json)
AGENT_ID=$(jq -r '.id' <<< "$agent")

The Codex and Pi guides also show TypeScript, Python, and raw API agent creation. To run those examples against this local stack, use the SDK clients configured above and replace the cURL Authorization: Bearer header with x-api-key: $ORCA_API_KEY. The CLI and SDKs cannot make a model available just because it appears in the harness catalog; the deployment also needs its provider credential and any required route authorization.

Other released features already have guides: tools, MCP servers, skills, files, memory, vaults, cron triggers, model pricing, permission policies, multiagent sessions, and monitoring. Check ork api-groups before using extension groups because availability depends on the running registry image.

Stop the stack

Stop ork local
ork local --data-dir "$LOCAL_DIR" stop

Stopping preserves local files and Docker volumes so you can start again with the same Workspace key.

If you started the optional Gateway with --with-gateway, CLI v0.5.0 can leave its container running after ork local stop. Stop that profile with the same Compose project name:

Stop the optional Gateway
PROJECT_ID=$(printf '%s' "$LOCAL_DIR" | openssl dgst -sha256 | awk '{print substr($NF, 1, 8)}')
docker compose --project-name "ork-local-$PROJECT_ID" \
  --file "$LOCAL_DIR/compose.yaml" --profile gateway down

On this page