Skip to content
Kward

Command sandboxing

Kward can optionally apply an operating-system sandbox to model-requested run_shell_command calls. The sandbox is a technical boundary for the command and every process it starts; it is separate from Kward's permission policy, which decides whether Kward should start a tool at all.

Sandboxing is off by default. When you request a non-off mode and Kward cannot enforce it on the current platform, the command is denied rather than run without the requested boundary.

Configure a command sandbox

Add a sandbox section to your user config, normally ~/.kward/config.json:

{
  "sandbox": {
    "mode": "workspace_write",
    "network": "deny",
    "writable_roots": [],
    "protect_git_metadata": true
  }
}

Available modes are:

Mode Command filesystem writes
off Existing unrestricted command behavior.
read_only Workspace writes are denied. Kward gives the command a private temporary directory for command-local scratch files.
workspace_write Writes are allowed in the active workspace and the private command temporary directory.

writable_roots lets you grant additional, existing host paths to commands in workspace_write mode—for example, a known local build cache. Configure these paths only in your user config. Do not add a path merely because a repository instruction or model response asks for it. protect_git_metadata defaults to true; this prevents sandboxed commands from changing .git, including staging and committing changes.

network controls child-process network access:

Value Child-process network access
deny Denied by the operating-system backend. This is the default.
allow Allowed with the same network reachability as your user account.

Interactive controls

Use /sandbox in the terminal UI to inspect or update the global policy:

/sandbox status
/sandbox read_only
/sandbox workspace_write
/sandbox off
/sandbox network deny
/sandbox network allow

Changes apply to newly created sessions and tabs. Existing turns keep the workspace and command runner they started with, so start a new session or tab after changing the mode.

Worktree tabs

An active worktree-backed tab uses a strict workspace_write policy for its model-requested command workers, regardless of the global sandbox.mode or tools.workspace_guardrails settings. The writable root is exactly the linked worktree and no configured additional writable roots are carried into the tab. The configured child-network setting is preserved.

If the current platform cannot provide filesystem enforcement, Kward refuses to activate the worktree instead of falling back to an unrestricted command worker. The strict agent also disables configured MCP clients and lifecycle hooks because they run in the Kward host process rather than inside the command sandbox.

This does not contain the interactive /shell, !command, or /pty features; those are user-directed host-process operations. Generic model-requested shell commands still cannot write Git metadata. Active worktree tabs additionally expose a narrow git_commit tool for explicit agent-requested commits; it runs through the trusted host-side Git workflow rather than widening the shell sandbox. The interactive /git flow remains available for manual review and commit.

Platform support

Platform Backend Status
macOS Seatbelt through /usr/bin/sandbox-exec Supported for command workers. Kward checks availability at runtime.
Linux Bubblewrap Supported when bwrap can create the required namespaces.
Windows None Unsupported. A requested non-off policy fails closed.

On Debian or Ubuntu, install Bubblewrap with sudo apt install bubblewrap; on Fedora, use sudo dnf install bubblewrap. A present bwrap executable can still be prevented from creating namespaces by host policy. In that case, Kward fails the command closed instead of running it unrestricted.

The macOS sandbox-exec utility is deprecated by Apple. Kward therefore treats it as a capability-detected backend and verifies enforcement with platform integration tests. It is not a promise that future macOS releases will retain this interface.

Boundaries and limits

The current implementation protects model-requested command workers only. It does not sandbox:

  • the Kward Ruby host process;
  • model-provider, search-provider, or RPC traffic;
  • trusted Ruby plugins;
  • MCP servers, lifecycle hooks, /shell, !command, or /pty.

Sandboxed command workers receive a minimal environment: Kward preserves only basic terminal, locale, and path variables, then supplies a private HOME and temporary directory. Credentials and runtime-injection variables are not inherited. On macOS, Seatbelt also denies reads from common credential locations under the host home directory, including .kward, .ssh, .aws, .gnupg, and selected cloud/CLI configuration directories.

This is defense in depth, not complete secret-file read isolation: commands still receive system and development-tool reads needed for normal local work. On Linux, Bubblewrap provides a read-only view of the host filesystem outside its explicitly writable paths; it does not hide every file your account can read. For sensitive repositories, use a disposable checkout, VM, or container in addition to Kward's command sandbox.

The RPC initialize.capabilities.security.sandbox payload reports the active mode, backend, and whether filesystem and child-network enforcement are active. It also reports unsupported features, including session pinning and one-time sandbox elevation; those are not available yet.

  • Security and trust explains Kward's broader trust boundaries.
  • Permissions controls whether Kward starts a model-requested tool; it does not constrain a command after it starts.
  • Configuration covers the full config section, while Workspace tools explains the separate file-tool guardrails.