OpenShell is a managed sandbox backend: Apus delegates sandbox lifecycle to the openshell CLI and executes commands over SSH. The selected OpenShell gateway can manage sandboxes locally with Docker, Podman, or virtualization, or run them on separate infrastructure. This OpenShell gateway is distinct from the Apus Gateway, which continues to run the agent and host-side plugins.

The plugin reuses the same SSH transport and remote filesystem bridge as the generic SSH backend, and adds OpenShell lifecycle (sandbox create/get/delete/ssh-config) plus an optional mirror workspace sync mode.

Prerequisites

Install the CLI and configure an OpenShell gateway using the NVIDIA OpenShell documentation. Before configuring Apus, verify the OpenShell CLI as the same operating system user that runs the Apus Gateway:

openshell --version
openshell gateway list
openshell sandbox list

gateway list marks the active gateway with *. If no gateway is selected, run openshell gateway select <gateway-name>. To register an existing local gateway endpoint, run openshell gateway add http://127.0.0.1:8080 --local. Replace the endpoint with the address of your running gateway. For an authenticated remote gateway, follow its login flow with openshell gateway login <gateway-name>.

The Apus Gateway service must see the same OpenShell CLI, gateway registration, credentials, and OpenShell workspace selection as these preflight commands. A shell-only PATH or OPENSHELL_WORKSPACE setting does not automatically reach a background service.

Quick start

apus plugins install @openclaw/openshell-sandbox
{
  agents: {
    defaults: {
      sandbox: {
        mode: "all",
        backend: "openshell",
        scope: "session",
        workspaceAccess: "rw",
      },
    },
  },
  plugins: {
    entries: {
      openshell: {
        enabled: true,
        config: {
          from: "apus",
          mode: "remote",
        },
      },
    },
  },
}

Validate the configuration and restart the Apus Gateway:

apus config validate
apus gateway restart

On the next agent turn Apus creates an OpenShell sandbox and routes tool execution through it. Verify both the plugin and the effective sandbox:

apus plugins inspect openshell --runtime --json
apus sandbox list
apus sandbox explain
openshell sandbox list

apus sandbox list is empty until a sandboxed agent turn first needs a runtime.

Workspace modes

This is the most important OpenShell decision.

OpenShell also has a control-plane resource named a workspace. That is separate from the filesystem workspace described below: it scopes sandboxes, providers, policies, inference routes, and membership. Set plugins.entries.openshell.config.workspace to use an existing non-default OpenShell workspace. The plugin does not create OpenShell workspaces or manage their membership. When this setting is unset, the plugin preserves the OpenShell CLI's ambient OPENSHELL_WORKSPACE selection, or the CLI's default fallback when no ambient selection exists.

mirror (default)

plugins.entries.openshell.config.mode: "mirror" keeps the local workspace canonical:

Best for development workflows: local edits outside Apus show up on the next exec, and the sandbox behaves close to the Docker backend.

Tradeoff: upload + download cost on every exec turn.

External editors and other Gateway processes do not participate in that lock. Avoid changing the local workspace while a mirrored command is running, because its download can replace those external edits.

remote

mode: "remote" makes the remote workspace canonical:

Best for long-running agents and CI: lower per-turn overhead, and host-local edits cannot silently clobber remote state.

Editing files on the host outside Apus after the initial seed is invisible to the remote sandbox. Run apus sandbox recreate to re-seed.

Choosing a mode

mirror remote
Canonical workspace Local host Remote OpenShell
Sync direction Bidirectional (every exec) One-time seed
Per-turn overhead Higher (upload + download) Lower (direct remote ops)
Local edits visible? Yes, on next exec No, until recreate
Best for Development workflows Long-running agents, CI

Configuration reference

All OpenShell config lives under plugins.entries.openshell.config:

Key Type Default Description
mode "mirror" or "remote" "mirror" Workspace sync mode
command string "openshell" Path or name of the openshell CLI
from string "apus" Sandbox source for first-time create
gateway string unset OpenShell gateway name (top-level --gateway)
gatewayEndpoint string unset OpenShell gateway endpoint (top-level --gateway-endpoint)
workspace string unset Existing OpenShell workspace used for every CLI operation
policy string unset Path to a sandbox policy YAML file on the Apus Gateway host
providers string[] [] Provider names attached at sandbox creation (deduped, one --provider flag per entry)
gpu boolean false Request GPU resources (--gpu)
autoProviders boolean true Pass --auto-providers (or --no-auto-providers when false) during create
remoteWorkspaceDir string "/sandbox" Primary writable workspace inside the sandbox
remoteAgentWorkspaceDir string "/agent" Agent workspace mount path (read-only when workspace access is not rw)
timeoutSeconds number 120 Timeout for openshell CLI operations

remoteWorkspaceDir and remoteAgentWorkspaceDir must be absolute paths and stay under the managed roots /sandbox or /agent. Other absolute paths are rejected. Choose distinct, non-overlapping directories because Apus manages their contents independently. Previously configured overlapping roots remain accepted for upgrade compatibility.

timeoutSeconds applies to ordinary OpenShell CLI operations. Sandbox creation always receives at least 300 seconds so image builds and first-time provisioning are not cut short by the default 120-second command timeout.

policy is a file path, not a policy name or ID. Prefer an absolute path such as /etc/apus/openshell-policy.yaml. Relative paths are resolved from the agent's local workspace during sandbox creation. An explicit policy overrides the OpenShell CLI's OPENSHELL_SANDBOX_POLICY environment variable. When neither is set, OpenShell uses its normal policy selection and defaults.

providers names existing OpenShell credential providers in the selected OpenShell workspace. With autoProviders: true, OpenShell may create missing providers from credentials already available to the Gateway process. With autoProviders: false, create required providers first and verify them with openshell --workspace <workspace-name> provider list. Keep API keys in OpenShell providers rather than adding them to sandbox environment variables.

workspace must match OpenShell's current workspace-name contract: 1-19 lowercase alphanumeric characters or single hyphens, with no leading, trailing, or consecutive hyphen. Create it first with openshell workspace create --name <name>. OpenShell rejects sandbox operations when the selected workspace does not exist or is being deleted. Set it to "default" to override an ambient non-default OpenShell workspace explicitly.

The setting applies to every OpenShell sandbox managed by this plugin instance. It cannot select different OpenShell workspaces per Apus agent or session. Changing it does not migrate existing sandboxes. Delete Apus's OpenShell sandboxes while the old workspace is still configured, then change the setting and restart the Gateway.

Sandbox-level settings (mode, scope, workspaceAccess) live under agents.defaults.sandbox like any backend. See Sandboxing for the full matrix.

To pass non-secret environment values into sandboxed commands, use the existing agents.defaults.sandbox.docker.env setting. The OpenShell backend also applies those values during command execution. OpenShell does not currently inject them into sandbox creation or background services. Keep credentials in OpenShell providers or another dedicated secret-delivery mechanism.

Examples

Minimal remote setup

{
  agents: {
    defaults: {
      sandbox: {
        mode: "all",
        backend: "openshell",
      },
    },
  },
  plugins: {
    entries: {
      openshell: {
        enabled: true,
        config: {
          from: "apus",
          mode: "remote",
        },
      },
    },
  },
}

Mirror mode with GPU

{
  agents: {
    defaults: {
      sandbox: {
        mode: "all",
        backend: "openshell",
        scope: "agent",
        workspaceAccess: "rw",
      },
    },
  },
  plugins: {
    entries: {
      openshell: {
        enabled: true,
        config: {
          from: "apus",
          mode: "mirror",
          gpu: true,
          providers: ["openai"],
          timeoutSeconds: 180,
        },
      },
    },
  },
}

Per-agent OpenShell with custom gateway

{
  agents: {
    defaults: {
      sandbox: { mode: "off" },
    },
    entries: {
      researcher: {
        sandbox: {
          mode: "all",
          backend: "openshell",
          scope: "agent",
          workspaceAccess: "rw",
        },
      },
    },
  },
  plugins: {
    entries: {
      openshell: {
        enabled: true,
        config: {
          from: "apus",
          mode: "remote",
          gateway: "lab",
          gatewayEndpoint: "https://lab.example",
          workspace: "research",
          policy: "/etc/apus/openshell-policy.yaml",
        },
      },
    },
  },
}

Lifecycle management

# List all sandbox runtimes (Docker + OpenShell)
apus sandbox list

# Inspect effective policy
apus sandbox explain

# Recreate (deletes remote workspace, re-seeds on next use)
apus sandbox recreate --all

# Recreate only one agent or the exact session scope shown by sandbox list
apus sandbox recreate --agent researcher
apus sandbox recreate --session "agent:researcher:main"

For remote mode, recreate is especially important: it deletes the canonical remote workspace for that scope, and the next use seeds a fresh one from local. For mirror mode, recreate mainly resets the remote execution environment since local stays canonical.

Sandbox list and recreate commands activate the configured backend's owning plugin plus the owner of each recorded runtime before inspecting or deleting it. Unrelated plugins are not loaded for these operations, and browser-only commands remain independent of the OpenShell backend.

Apus keeps a registered sandbox's shipped legacy runtime name after an upgrade so its remote workspace remains addressable. Recreating that scope deletes the legacy runtime. The next use creates the current 19-character runtime name.

OpenShell v0.0.92 can still locate a sandbox record created by v0.0.68, but a Docker-backed sandbox may remain in a non-Ready phase after the gateway upgrade. Apus preserves the registered runtime identity, refuses to create a replacement implicitly, and reports the scoped apus sandbox recreate command. Treat that recreation as destructive in remote mode because the remote workspace is canonical.

Recreate after changing any of:

When changing the OpenShell gateway or OpenShell workspace, recreate the affected sandboxes while the old gateway and workspace are still selected. Otherwise cleanup targets the new location instead of the existing sandbox.

If OpenShell cannot delete a sandbox, Apus reports the failure and keeps the runtime registry entry so recreation or pruning can be retried safely. Restore the original gateway, workspace, authentication, and connectivity, inspect the runtime with openshell --workspace <workspace-name> sandbox get <sandbox-name>, and rerun the scoped apus sandbox recreate command. Do not switch the configured workspace or delete the registry entry to hide the failure.

Security hardening

The mirror-mode filesystem bridge pins the local workspace root and rechecks canonical paths (via realpath) before every read, write, mkdir, remove, and rename, rejecting mid-path symlinks. A symlink swap or a remounted local workspace cannot redirect file access outside the mirrored tree.

Workspace synchronization excludes .git, hooks, and git-hooks in both directions. Repository credentials, history, and trusted hook code remain on the Apus Gateway host instead of being copied into an untrusted sandbox.

Mirror synchronization never copies entries it cannot represent, such as symlinks, FIFOs, or Unix sockets, into either workspace. Existing host entries of those types remain intact at every depth, along with their parent directories, even if the sandbox deletes those directories or replaces them with files. Remote replacements that conflict with these preserved host paths are ignored. Ordinary files and directories still receive remote changes and deletions.

Local filesystem inspection errors, such as permission or I/O failures, stop the affected synchronization instead of treating the path as missing. Fix the reported filesystem error before retrying; materialized skills are inspected before the existing remote skills view is cleared.

Custom image contract

The OpenShell source image owns the remote operating system and package set. Apus does not apply Docker image, root-filesystem, network, user, or package settings to this backend.

Custom images used with the Apus filesystem bridge must provide:

When the agent workspace differs from the sandbox workspace, the sandbox user and policy also need write access to both configured remote roots. Standard unprivileged images often cannot create the default /agent directory. Either create and grant access to /agent in the image and policy, or configure two non-overlapping directories beneath the already-writable sandbox root:

{
  plugins: {
    entries: {
      openshell: {
        enabled: true,
        config: {
          remoteWorkspaceDir: "/sandbox/workspace",
          remoteAgentWorkspaceDir: "/sandbox/agent",
        },
      },
    },
  },
}

Package installation and private certificate roots must be included in the source image or installed from inside the sandbox. The selected OpenShell policy must permit the required network destinations, and the sandbox user and filesystem must permit the writes. sandbox.docker.network, sandbox.docker.readOnlyRoot, sandbox.docker.user, and sandbox.docker.setupCommand do not configure OpenShell.

Current limitations

Troubleshooting

First separate Apus Gateway health, plugin activation, and OpenShell gateway connectivity:

apus gateway status --deep --require-rpc
apus plugins inspect openshell --runtime --json
apus sandbox explain
apus sandbox list
openshell gateway list
openshell sandbox list
apus logs --follow

How it works

  1. Apus runs sandbox get for the sandbox name (with the selected OpenShell workspace and any configured --gateway/--gateway-endpoint). If that fails it creates one in the same OpenShell workspace with sandbox create, passing --name, --from, --policy when set, --gpu when enabled, --auto-providers/--no-auto-providers, and one --provider flag per configured provider.
  2. Apus runs sandbox ssh-config for the sandbox name to fetch SSH connection details.
  3. Core writes the SSH config to a temp file and opens an SSH session through the same remote filesystem bridge as the generic SSH backend.
  4. In mirror mode: sync local to remote before exec, run, sync back after.
  5. In remote mode: seed once on first use, then operate directly on the remote workspace.

Related