Environment Spec Referencepublic

Last verified 23 Sep 2026

DigitalOcean Harness Runtime combines the functionality of a lightweight microVM, built-in tools like chromium and a coding sandbox needed by agents to do work. The product offers rich lifecycle APIs that persist conversational history and working state across sessions, with pause/resume/fork semantics so that developers can control costs and adapt workflows to the nonlinear quirks of agentic work. See What You Can Build for example use cases.

As an alternative to configuring your environment in the Control Panel, you can define an environment specification using YAML.

Field names are decoded strictly. An unknown or misspelled field returns a 400 naming the key, so nothing you write is silently ignored.

Example Environment Specification

The following spec shows a named Codex session with size and idle timeout, a persistent workspace, env and secrets, an egress allowlist, Action Gateway and custom MCP tools, inline skills, permission rules, and interactive mode.

name: my-first-session
agent: codex
size: mars-2vcpu-4gb
idle_timeout: 10m
persistent_workspace: true
env:
  MODEL: gpt-5.4
secrets:
  OPENAI_API_KEY: ${OPENAI_API_KEY}
egress:
  - api.github.com
  - pypi.org
  - mcp.example.com        # inline MCP hosts are NOT auto-allowed
tools:
  # One entry per server. `- do.actions` (all tools) OR `- do.actions: [...]`
  # (selection). Declaring both is a duplicate-server 400.
  - do.actions: [exa_web_search, action_code, "toolbelt:read-only@1"]
  - name: my-mcp
    url: https://mcp.example.com
    tools: [search, fetch]
    auth:
      header: Authorization
      secret: MY_MCP_TOKEN
skills:
  - name: pdf-processing
    description: "Use when the task involves reading or editing PDF files."
    instructions: |
      # PDF Processing
      Steps the agent should follow...
    license: Apache-2.0
    metadata:
      author: platform-team
    allowed_tools: [file.read, bash]
permissions:
  default: ask
  rules:
    - tool: bash
      action: ask
      enforcement: strict          # strict | best-effort
    - tool: file.read
      action: allow
    # Server-qualified MCP targets; server must be declared in `tools`.
    - tool: do.actions/exa_web_search
      action: allow
    - tool: my-mcp/create_issue
      action: ask
mode: interactive

Top-Level Fields

The environment specification has the following top-level fields.

Field Type Required Description
agent string Yes Adapter identifier. See agent.
name string No Session name. Team-unique among live sessions, 1-64 characters of letters, digits, -, ., or _, starting and ending alphanumeric, and not in UUID form. Generated when not provided.
description string No Free-form, up to 1024 characters.
labels map No Free-form labels.
image / entrypoint string / list With agent: custom Digest-pinned container image and argv entrypoint for a bring-your-own container.
config object No Provider-specific runtime config. Use only with agent: codex-agentapi. Forwarded as the OpenAI Agents API create-session body; DigitalOcean stores and echoes it without validating contents.
repos list No GitHub OWNER/REPO entries, unique case-insensitively. The first entry is the session repo_hint. The full list reaches the guest as HARNESS_WORKSPACE_REPOS and as the platform workspace-repos skill. Nothing is cloned for the agent, and egress must still permit github.com.
env map No Non-secret plaintext guest environment. See env.
secrets map No Declared credentials. See secrets.
size string No Machine size slug. When omitted, the service default applies (currently mars-2vcpu-4gb). Available sizes are mars-1vcpu-1gb, mars-2vcpu-2gb, mars-2vcpu-4gb, mars-4vcpu-8gb, and mars-16vcpu-32gb. Use GET /v2/agents/sessions/sandbox/sizes for the current list. See Sandbox Sizes.
idle_timeout duration No Idle suspend for this session. Defaults to 15 minutes. Mutually exclusive with keep_warm.
keep_warm boolean No Exempt this session from idle auto-pause. See keep_warm.
egress string, list, or object No Outbound network policy request. See egress.
tools list No MCP server attachments. See tools.
permissions object No Tool-permission policy. See permissions.
template string No Execution-environment template. When not provided, the value is derived from the adapter (for example codex to coding-codex).
persistent_workspace boolean No Keep /workspace across idle suspend and resume.
skills list No Inline agent skills. See skills.
mode / serving string / object No Lifecycle mode. See mode and serving.

Durations are strings of integer hour, minute, or second components (10m, 90s, 8h, 1h30m). Bare second counts such as 600 are not allowed.

agent

The agent field accepts the following adapter identifiers. For the architectural difference between them, see Agent Adapters.

Value Description
claude-code Runs Claude Code under DigitalOcean supervision.
opencode Runs OpenCode under DigitalOcean supervision.
codex Runs Codex CLI under DigitalOcean supervision.
codex-agentapi Externally orchestrated session. An external agent provider owns the agent loop and DigitalOcean supplies the sandbox. Use config to configure the session. See Agent Adapters and Run an OpenAI Agents API Session. See also Connect a DigitalOcean sandbox to an Agents API session.
langgraph Runs a customer LangGraph application. See Run a LangGraph Agent.
hermes Runs the Hermes gateway as a long-lived agent with an admin dashboard on loopback port 9119. See Run a Hermes Agent.
custom Runs a container image you supply. Requires image and entrypoint.

The deprecated alias codex-cli is still accepted and resolves to codex, with a warning.

config for codex-agentapi

Use config only with agent: codex-agentapi. DigitalOcean forwards it as the OpenAI Agents API create-session body, stores and echoes it, and does not validate its contents.

CLI and webhook-managed spec

Include the OpenAI create-session request in config. The CLI authenticates that request with OPENAI_API_KEY, fills ${ENV_ID} from the response, and injects the executor key as CODEX_API_KEY:

name: openai-codex-session
agent: codex-agentapi
config:
  agent:
    model: gpt-5.6-sol
    instructions: Work from the files in /workspace.
  environment:
    type: self_hosted
    workspace_directory: /workspace
egress:
  - api.openai.com
  - codex-cloud-environments.chatgpt.com
env:
  CODEX_ENVIRONMENT_ID: ${ENV_ID}
secrets:
  CODEX_API_KEY: ${OPENAI_EXECUTOR_API_KEY}

Application-managed spec

When the application creates the OpenAI session first, omit config from the sandbox-only spec:

agent: codex-agentapi
egress:
  - api.openai.com
  - codex-cloud-environments.chatgpt.com
env:
  CODEX_ENVIRONMENT_ID: ${ENV_ID}
secrets:
  CODEX_API_KEY: ${OPENAI_EXECUTOR_API_KEY}

Pass the OpenAI session ID in params.openai_session_id and supply ENV_ID and OPENAI_EXECUTOR_API_KEY through body.variables when calling client.agents.create_session.

Provider-specific fields

Field Description
agent Set to codex-agentapi. Selects the DigitalOcean image that includes Codex and starts codex exec-server.
config OpenAI create-session request for CLI and webhook-managed flows. Application-managed flows can omit it.
config.agent OpenAI model and agent instructions.
config.environment.type Set to self_hosted.
config.environment.workspace_directory Set to /workspace.
env.CODEX_ENVIRONMENT_ID OpenAI environment ID. The CLI fills ${ENV_ID} after creating the OpenAI session. Applications and webhook controllers supply it through the DigitalOcean create request.
secrets.CODEX_API_KEY OpenAI environment key. Supply OPENAI_EXECUTOR_API_KEY, not the key used for other OpenAI API operations.
egress The platform includes the OpenAI executor hosts for this adapter. Spec examples often list api.openai.com and codex-cloud-environments.chatgpt.com for clarity. Add destinations required by the agent’s tools.

Use OPENAI_API_KEY for OpenAI API requests made by the application, controller, or CLI. Use a separate OPENAI_EXECUTOR_API_KEY environment key for the executor connection. Both keys must have the same owner, organization, and project. Place the executor key under secrets, not env, and do not log or save the resolved manifest. See Secrets and Configuration.

mode and serving

mode is the lifecycle discriminator.

Value Description
interactive Default. One session to one sandbox. serving is not allowed.
served Automatically scaled fleet. serving is required.

When mode: served, the serving object takes the following fields.

Field Type Required Description
min integer Yes Minimum number of sandboxes to keep running.
max integer Yes Maximum number of sandboxes to scale to.
target_concurrency integer Yes Sessions per sandbox to target before scaling out.
scale_to_zero.idle duration No Idle period before the fleet scales to zero.

keep_warm

keep_warm: true exempts a session from idle auto-pause entirely. It exists because idle_timeout cannot express “never”: omitting it means “use the team default” and 0 means the same, so no duration disables the timer.

Declaring both idle_timeout and keep_warm is rejected rather than resolved in favor of one, because the timeout would otherwise silently never elapse.

Omitting keep_warm leaves the choice to the adapter. Adapters whose value is an always-available interactive surface default to staying warm, such as hermes, whose dashboard is reachable through port forwarding only while the sandbox runs. Every other adapter defaults to pausing on idle. Setting keep_warm: false opts a hermes session back into pausing.

A warm session bills for its sandbox until you stop it explicitly, and a request to pause one returns a 409. The policy applies to manual pause as well as the automatic path.

agent: hermes
keep_warm: true

egress

egress requests the session’s outbound network policy. Omitting it leaves the session unrestricted. Setting an allowlist turns it on, and everything unnamed is then denied. The field takes exactly one of the following forms.

Form Example Meaning
Omitted no egress field Unrestricted. No platform defaults are added.
Scalar egress: unrestricted The same, stated explicitly. Cannot be combined with an allowlist or a VPC.
List egress: [pypi.org, api.github.com] Hostname allowlist. Put IP literals in the object form instead.
Object the fields below Allowlist and VPC path.

The object form takes the following fields.

Field Type Required Description
allow_hosts list No Hostnames the sandbox may reach. Naming any host turns the allowlist on.
allow_ips list No Exact IPv4 or IPv6 literals, not CIDR ranges. Matches IP-addressed traffic only, so a hostname resolving to an allowed IP is still denied unless it is in allow_hosts.
vpc_uuid string No Send egress through this VPC instead of public NAT. Your team must own it, and it must be in a supported region. Cannot change after the session is created.
subnet_uuid string No Subnet within vpc_uuid. Rejected without vpc_uuid.

When the allowlist is on, the platform merges in the hosts the session needs to function: platform hosts such as api.github.com, github.com, and registry.digitalocean.com, the hosts the adapter requires, and the configured model inference endpoints. Omitting egress adds none of these, because there is no allowlist to add to. An allowlist built only from allow_ips also does not receive them, and session creation returns a warning in that case.

The request is not the final policy. The platform intersects it with team and platform policy, which can narrow what you ask for but never widens it.

Inline MCP hosts are not reliably added to the allowlist. List them under egress as well. Tools reached through Action Gateway need no entry, because the gateway calls the provider rather than the sandbox doing so. For the wider model, see Network Egress.

env

Non-secret plaintext config merged into the guest environment. The guest environment is debug-readable, so use secrets for credentials.

Rule Behavior
Reserved platform keys Rejected at parse, including TEAM_ID, SESSION_ID, SANDBOX_ID, and related sandbox control keys.
Credential-shaped key names Keys such as API_KEY, TOKEN, and SECRET are rejected on durable config create. Declare them under secrets.
Credential-shaped values Values carrying a known credential prefix are rejected on durable config create.

secrets

Every credential the agent needs, declared in one map. The slot name is the guest environment variable. doctl expands ${VAR} placeholders client-side before upload, and an unexpanded ${VAR} or $VAR value is rejected.

secrets:
  OPENAI_API_KEY: ${OPENAI_API_KEY}
  DATASETS_ACCESS_KEY:
    value: ${SPACES_ACCESS_KEY}
    env: false
  DATASETS_SECRET_KEY:
    value: ${SPACES_SECRET_KEY}
    env: false

A slot value takes one of the following forms.

Form Description
String Write-only tenant secret. DigitalOcean extracts it at create time and never persists it in the stored manifest, returns it on GET, or captures it in an audit snapshot.
Object Long form of the shorthand. See the fields below.

The object form takes the following fields.

Field Type Required Description
value string Yes The credential, usually a ${VAR} placeholder.
env boolean No Defaults to true. Set false to keep the slot mount-only and out of the guest environment. Mount credentials must use env: false.
url string No Binds the credential to one HTTPS destination, making the slot a scoped secret. The sandbox receives an authorization handle under the slot name instead of the credential, and the egress proxy attaches the real credential to requests for this destination.
secrets:
  PROVIDER_API_KEY:
    value: ${PROVIDER_API_KEY}
    url: https://api.example.com

url accepts https:// with one exact hostname and no port, query, fragment, or embedded credentials. Wildcards, IP literals, localhost, and metadata addresses are rejected, and a hostname matches only itself rather than its subdomains. A path is optional, is treated as a prefix, must start with /, must stay under 256 characters, and cannot contain ?, #, or %.

An environment can declare at most 16 scoped secrets. A slot cannot combine url with env: false, because the handle has to reach the guest environment. The bound host is added to the egress allowlist automatically.

Redemption depends on infrastructure support that is not yet enabled everywhere. Where it is missing, the sandbox still receives a handle rather than the credential, and requests to the bound host fail to authenticate. See Secrets and Configuration.

For guidance on choosing between env and secrets, see Secrets and Configuration.

permissions

The permission policy for the environment. It applies when the session starts and is checked against what the chosen adapter supports. If you omit permissions, the platform uses its built-in default. For native Harness Runtime actions, the last matching rule in the list wins, and if no rule matches, the action uses default.

A rule naming a tool the adapter does not declare is not silently ignored. Under the default strict enforcement a deny or ask rule on an undeclared tool fails session create, while an allow rule degrades to a warning. This matters most with codex, which declares only bash. Two parts of the taxonomy are currently declared by no adapter at all, so a rule naming them can only ever warn or block: git.* and web.fetch. Set enforcement: best-effort on a rule you want downgraded to a warning rather than treated as a create-time error.

Field Type Required Description
default string No allow, ask, or deny. Defaults to ask.
rules[].tool string Yes Harness taxonomy (bash, file.read, file.write, web.fetch, git.*, mcp, custom) or a server-qualified MCP target (<server>/<tool> or <server>/toolbelt:<name>[@<int>]). The server must be declared in tools.
rules[].match map No Matcher map, for example command: "rm -rf *". Values must not contain $VAR expansions.
rules[].action string Yes allow, ask, or deny.
rules[].enforcement string No strict or best-effort. Defaults to strict for deny and best-effort otherwise. For gateway-routed MCP rules, enforcement is always strict.
filesystem.mode string No read-only or workspace-write.
filesystem.allow_write list No Extra writable paths.
network.default / network.allow string / list No Narrows within egress, never widens it.

Action Gateway evaluates its projected tool rules separately, using most-specific matching rather than list order. An ask decision there requires the client to complete MCP elicitation or a separate approval request, so do not assume every adapter supports an inline prompt. See Permission Policies and Action Gateway Tool Policies.

tools

Requests MCP server attachments. Each list entry is exactly one of two forms.

Form Description
Catalog - do.actions for all tools, or - do.actions: [tool, ...] for a selection. The do.* namespace is reserved for DigitalOcean-owned servers, and do.actions is Action Gateway. Selection entries are individual tool names with an optional integer version pin (exa_web_search or exa_web_search@2) or toolbelt:<name>[@<int>] group references. toolbelt references are valid on catalog entries only.
Inline A name plus an https:// url, for an MCP server you host. See the fields below.

An inline entry takes the following fields.

Field Type Required Description
name string Yes 1-64 characters of lowercase letters, digits, -, ., or _, with alphanumeric ends. Must not start with do..
url string Yes An https:// URL. The host is not auto-allowed, so list it under egress.
tools list No Tool selection for this server. Accepted and returned with a warning, but it does not yet restrict what the server advertises.
auth.secret string No Names a declared, env-visible secrets slot. A slot with env: false is rejected.
auth.header string No Defaults to Authorization.
tools:
  - do.actions: [exa_web_search, action_code, toolbelt:read-only@1]
  - name: internal-jira
    url: https://mcp.example.com/mcp
    tools: [create_issue]
    auth:
      header: Authorization
      secret: JIRA_MCP_TOKEN

Attaching a tool does not grant permission to call it. Allow, ask, and deny dispositions stay in permissions. See Connect Action Gateway to Harness Runtime.

skills

Inline agent skills in the Agent Skills (SKILL.md) shape. A spec accepts a maximum of 32 entries. instructions are capped at 32 KiB per skill and 256 KiB total across the list, and the encoded guest environment value must also fit the 64 KiB variable cap. workspace-repos and sandbox-access are platform-reserved names.

Field Type Required Description
name string Yes Unique. 1-64 characters of lowercase letters, digits, and single hyphens.
description string Yes Load-bearing selection text that tells the agent when to load the skill. Write it as “Use when …”. Maximum 1024 characters.
instructions string Yes Markdown body.
license string No Standard passthrough.
metadata map No Free-form map rendered into front matter.
allowed_tools list No Advisory scoping only. Enforcement stays in permissions.

We can't find any results for your search.

Try using different keywords or simplifying your search terms.