nanobot/docs/COMMAND_WRAPPER.md
chengyongru 5257453c4c feat(exec): add command_wrapper for sandbox support
Allow users to wrap shell commands in a sandbox (e.g. bubblewrap,
firejail, nsjail) via config, without nanobot baking in any sandbox
backend. The template supports {command} and {cwd} placeholders with
plain string replacement.
2026-03-31 23:47:23 +08:00

2.9 KiB

Exec Tool Sandbox (commandWrapper)

The tools.exec.commandWrapper config option wraps every shell command in a user-defined template before execution. This allows you to add a sandbox layer (e.g. bubblewrap, firejail, nsjail) without any code changes to nanobot.

Configuration

{
  "tools": {
    "exec": {
      "commandWrapper": "<template>"
    }
  }
}

Leave empty (the default) to run commands directly with no wrapper.

Placeholders

Two placeholders are available in the template:

Placeholder Value
{command} The original shell command generated by the LLM
{cwd} Absolute path of the working directory

nanobot performs plain string replacement — it does not parse, validate, or shell-escape the values. The wrapper template is trusted configuration.

Examples

bubblewrap

{
  "tools": {
    "exec": {
      "commandWrapper": "bwrap --ro-bind /usr /usr --ro-bind-try /bin /bin --ro-bind-try /lib /lib --ro-bind-try /lib64 /lib64 --proc /proc --dev /dev --tmpfs /tmp --bind {cwd} {cwd} --chdir {cwd} -- sh -c \"{command}\""
    }
  }
}

Requires: apt install bubblewrap (or equivalent for your distro).

firejail

{
  "tools": {
    "exec": {
      "commandWrapper": "firejail --noprofile --private={cwd} -- {command}"
    }
  }
}

nsjail

{
  "tools": {
    "exec": {
      "commandWrapper": "nsjail -Mo --chroot /sandbox --cwd {cwd} -- {command}"
    }
  }
}

Caveats

Warning

Do not wrap {command} in shell quotes. If the original command contains the same quote character, the shell will break the quoting context. For example, sh -c '{command}' will fail on any command that contains single quotes.

This is an inherent limitation of the template approach — nanobot substitutes {command} as a raw string and cannot safely shell-quote it (the command may contain compound syntax like &&, |, ; that must be preserved for the inner shell).

Interaction with create_subprocess_shell

nanobot executes the wrapped command via create_subprocess_shell, which adds an outer shell layer. Keep this in mind when designing your template:

  • Without sh -c (e.g. firejail ... -- {command}): The outer shell parses {command} directly. Compound commands with && and | work as expected because they are parsed by the outer shell before the sandbox tool receives them.
  • With sh -c (e.g. bwrap ... -- sh -c "{command}"): The command is passed through two shell layers. This is only needed if the sandbox tool requires a single command argument but you want to support compound syntax.

restrict_to_workspace is independent

The tools.restrictToWorkspace setting and commandWrapper are orthogonal features. The workspace restriction guards against path traversal in the original command (before wrapping). The sandbox wrapper provides OS-level isolation. You can use either or both — they address different threat models.