Catalog PyPI
PACKAGE EXPLORERView in registry

agent-nook

PyPI

A lightweight bwrap sandbox for AI agents

Versions collected1
Direct dependencies6From release 0.1.2
Last observed20 Oct, 22:45 UTC

README

Version 0.1.2 documentation

Agent Nook — Simple Agent Sandbox

A lightweight sandbox for AI agents using bubblewrap (bwrap) to isolate the agent from the host system.

Lightweight. Fast. No special privileges. Easy to install.

License: GPL v3 GitHub

What is this?

A simple sandbox for AI agents, built on top of bubblewrap — the low-level sandboxing tool at the core of Flatpak.

Unlike full container systems (podman, docker, apptainer), this is:

  • No dedicated image required — sandbox starts from the host filesystem
  • No root required — works with unprivileged users
  • Lightweight & fast — minimal overhead, no daemon required
  • Easy to install — python based, nearly zero dependencies (Python + PyYAML), pip or pipx install
  • No online services interaction — does need to download recipes or templates online. Should be simple enough to setup the configuration from the examples

This project has similarities and shares many goal with the following projects, which you can consider as alternative is you wish:

While the main driver for writing this project is the sandboxing / isolation of AI agents, it can be used to sandobox pretty much anything.

Why?

AI Agents usually run with the same UID / GID as your daily user. This means that discretionary access control (DAC) has no way to distinguish it from the human behind the screen. You might not want to share all of your data with the AI Agent, or let it change files in your home directory. As we all know sometimes AI makes mistakes, like in this case, which happened during the development of this very project:

Screenshot_20260908_131001

the agent tried to rm -rf ~/.config that would have wiped a lot of important files in my personal home directory. Luckily opencode stopped the execution and asked as it detected this was not within the project directory. However, there are easy bypass for the AI agent, and I might not be so lucky next time. However, I still want to share data with the agent, for example the git repo itself so it can help me coding. This tool is meant to give you the possibility of decide how to isolate the agent and what to share with it. You can even force it to be unable to use the network, if you like. However, I would discourage considering this tool a bulletproof security tool. The main objective is to control what the AI agent can do / access, but it assume the agent itself is trust-able software, that might make, unintentional, mistakes.

Important notes!

This is a project I do during my spare time. It may contain errors or security bugs, so use it at your own risk and don't run untrusted / malicious code within the sandbox. It might find a way out and eat your hamster.

Special thanks

This project is being developed with the assistance of AI open weight models and a lot of free and open software. With all of the following (plus more) this would not have been possible. A big thank you to everybody!

  • Qwen AI model for providing the open weight model.
  • Oh My Pi, Qwen-code and Opencode the AI coding agents I used for this project.
  • Lemonade AI server for the easy to use personal and local AI server.
  • AMD ROCm for the great Linux support. Using ROCm on Fedora worked out of the box and it was a trouble free experience.
  • Fedora Plasma Desktop edition, for making a great Linux distro and packaging ROCm and llama-cpp making it a trouble free installation experience.
  • GNU for start and promoting important free software projects.
  • Linux For providing the kernel to run all of this.

Quick Start

git clone https://github.com/enricotagliavini/agent-nook
cd agent-nook
pip install -e .
agent-nook run --command "echo hello from sandbox"

Design Decisions

ConcernChoiceWhy
SandboxingBubblewrapLow-level, no root, no daemon, Flatpak-proven
ImagesOptionalKeep it simple; the sandbox can use host filesystem, or, if desired, also a custom image
RuntimeNonebwrap has no background service
Config formatYAMLHuman-readable, standard for config files
Logginglogging module + XDGStandard library, no dependencies
LanguagePython 3.10+Type hints, dataclasses, modern features

How it works

Agent → bwrap sandbox → isolated filesystem + capabilities → agent runs safely

Bubblewrap applies filesystem isolation, network isolation, and capability restrictions in userspace — no kernel namespaces or privileged containers needed.

What bubblewrap does

Bubblewrap creates a userspace sandbox by:

  1. Filesystem isolation: Mounts a temporary root filesystem and selectively overlays host directories via bind mounts
  2. Namespace isolation: Separates processes into isolated PID, UTS, IPC, cgroup, user, and network namespaces
  3. Capability restrictions: Drops dangerous Linux capabilities by default
  4. Session isolation: Creates a new session (--new-session) to prevent TIOCSTI attacks
  5. Parent binding: Ensures sandbox dies with parent (--die-with-parent)

Mount types

Seven mount types are supported:

TypeSyntaxDescription
bind--bind SRC DESTRead-write bind mount
ro-bind--ro-bind SRC DESTRead-only bind mount
dev-bind--dev-bind SRC DESTDevice bind mount
tmpfs--tmpfs TARGET [SIZE]Temporary filesystem in memory
proc--proc TARGETProc filesystem
dev--dev TARGETDev filesystem
dir--dir TARGETDirectory without content check

Namespace isolation

Six namespace types can be unshared (isolated):

NamespaceFlagDescription
PID--unshare-pidIsolate process tree
UTS--unshare-utsIsolate hostname/domainname
IPC--unshare-ipcIsolate inter-process communication
cgroup--unshare-cgroupIsolate cgroup hierarchy
user--unshare-userIsolate user IDs
network--unshare-netIsolate network stack (also implies UTS and IPC)

Note: Specifying --unshare-net automatically enables --unshare-uts and --unshare-ipc.

Example bwrap command

bwrap \
  --die-with-parent \
  --new-session \
  --bind /host/data /sandbox/data \
  --ro-bind /host/config /sandbox/config \
  --cap-drop ALL --cap-add CAP_CHOWN CAP_DAC_OVERRIDE \
  --unshare-pid --unshare-uts --unshare-ipc --unshare-cgroup --unshare-user --unshare-net \
  --chdir /sandbox \
  python3 agent.py

Mount ordering

Mounts are applied in topological order (parents before children). For example:

mounts:
  - target: /home
    type: tmpfs
  - source: /home/user/project
    target: /home/project
    type: bind

The sandbox will correctly mount /home first, then /home/project as a subdirectory, irrespective of the order in which they are specified in the configuration file.

Installation

It is recommended to use the bubblewrap version that comes from the OS. If you OS supports flatpak bubblewrap should be already installed. If there is bubblewrap available, install adding the optional [bubblewrap] dependencies to pull a binary build from PyPi.

From source

git clone https://github.com/enricotagliavini/agent-nook
cd agent-nook
pip install -e ".[dev]"  # with dev dependencies
pip install -e ".[bubblewrap]"  # with bubblewrap dependency in case it's not already installed and available by the OS

With pip

pip install agent-nook
agent-nook run --command "echo hello"

With pipx

pipx install agent-nook
agent-nook run --command "echo hello"

CLI Usage

Available subcommands

CommandDescription
runRun a command in a sandbox
statusShow agent-nook status
logsShow recent logs (live follow with -f)
listList available sandboxes

Running a command in a sandbox

# Basic usage
agent-nook run python3 my_script.py

# With a Python inline script
agent-nook run python3 -c "print('hello')"

# With bind mounts
agent-nook run --bind /host/data:/sandbox/data python3 script.py

# Read-only bind mount
agent-nook run --ro-bind /host/config:/sandbox/config python3 script.py

# Custom config file
agent-nook run --config /path/to/config.yaml python3 script.py

# Override capabilities
agent-nook run --cap-add NET_BIND_SERVICE --cap-add SYS_PTRACE python3 net_script.py

# Drop capabilities
agent-nook run --cap-drop SYS_ADMIN --cap-drop SYS_PTRACE python3 safe_script.py

# Unshare namespaces (space-separated)
agent-nook run --unshare pid uts ipc python3 script.py

# Environment variables
agent-nook run --env PATH=/usr/bin --env HOME=/root python3 script.py

# Unset environment variables
agent-nook run --unset-env PATH --unset-env HOME python3 script.py

# Custom hostname
agent-nook run --hostname my-agent python3 script.py

# Work in specific directory
agent-nook run --chdir /home/enrico python3 my_script.py

# Work directory from config
agent-nook run python3 my_script.py

# Quiet mode (suppress non-error output)
agent-nook run --quiet python3 my_script.py

Viewing logs

# Show the last 50 lines
agent-nook logs

# Show the last 100 lines
agent-nook logs -n 100

# Follow the log live (like `tail -F`); stop with Ctrl+C
agent-nook logs -f

# Follow, starting from the last 100 lines (`-F` / `--follow` work too)
agent-nook logs -F -n 100

CLI Reference

FlagDescription
CMD [ARGS...]Command to execute in the sandbox (e.g. python3 agent.py)
--config CONFIGOverride config file path (default: ~/.config/agent-nook/sandbox.yaml)
--chdir DIRChange working directory in sandbox (overrides config)
--cap-add CAPAdd capability (e.g. CAP_NET_BIND_SERVICE, repeat for multiple)
--cap-drop CAPDrop capability (e.g. CAP_SYS_ADMIN, repeat for multiple)
--unshare NSUnshare namespace (e.g. pid uts ipc, repeat for multiple)
--bind SRC:DESTBind mount host SRC to sandbox DEST (repeat for multiple)
--ro-bind SRC:DESTRead-only bind mount host SRC to sandbox DEST (repeat for multiple)
--env KEY=VALUESet environment variable (repeat for multiple)
--unset-env KEYUnset environment variable (repeat for multiple)
--hostname HOSTNAMESet sandbox hostname
--new-sessionCreate new session (prevents TIOCSTI attacks, default: on)
--no-new-sessionDon't create new session (allows TIOCSTI)
--quietSuppress non-error output
--die-with-parentKill sandbox child when parent dies (default: on)
--no-die-with-parentKeep sandbox alive after parent exits

Configuration

Configuration follows XDG Base Directory Specification.

Where config lives

  • ~/.config/agent-nook/sandbox.yaml — Main sandbox configuration
  • ~/.config/agent-nook/logging.yaml — Logger configuration
  • ~/.local/state/agent-nook/ — Log files; lines are tagged with the active sandbox name (e.g., agent_nook[mybox])

Setting up config

To create or edit the configuration, edit ~/.config/agent-nook/sandbox.yaml directly.

The config uses YAML format with these top-level keys:

name: agent-sandbox
chdir: /home/enrico
mounts:
  - source: /home/enrico/project
    target: /workspace
    type: bind
capabilities:
  drop: ["ALL"]
  keep: ["CAP_CHOWN", "CAP_DAC_OVERRIDE"]
unshare:
  pid: true
  ipc: true
  uts: false
  user: true

You can override any config option at runtime via CLI flags:

agent-nook run --chdir /tmp --cap-add NET_BIND_SERVICE python3 agent.py

Default sandbox config

See sandbox.yaml

Customizing config

Edit ~/.config/agent-nook/sandbox.yaml to customize:

  • name: Sandbox identifier (used in logs)
  • chdir: Working directory inside the sandbox
  • mounts: Mounts from host to sandbox. At least one mount is required — a config with no mounts is rejected, because the sandbox root would be completely empty (you can also add one at runtime via --bind). See below for supported types.
  • capabilities: Linux capabilities to drop (drop: ["ALL"]) or keep (keep: ["CAP_CHOWN"])
  • unshare: Which namespaces to unshare (isolate) — pid, uts, ipc, cgroup, user, network
  • hostname: Sandbox hostname (implies UTS namespace unshare)
  • die_with_parent: Kill sandbox children when parent exits (default: true)
  • new_session: Create a new session (prevents TIOCSTI attacks, default: true)
  • timeout: Maximum execution time in seconds (null = no timeout)
  • env_vars: Environment variables to set (YAML map: KEY: value)
  • unset_vars: Environment variables to unset (YAML array: ["PATH", "HOME"])

Supported mount types

Seven mount types are supported:

# Read-write bind mount
mounts:
  - source: /host/path
    target: /sandbox/path
    type: bind

# Read-only bind mount
  - source: /host/config
    target: /sandbox/config
    type: ro-bind

# Device bind mount
  - source: /dev/sda
    target: /sandbox/dev
    type: dev-bind

# Temporary filesystem (in memory)
  - target: /tmp
    type: tmpfs

# with optional size
  - target: /sandbox/cache
    type: tmpfs
    size: "100M"

# Proc filesystem
  - target: /sandbox/proc
    type: proc

# Dev filesystem
  - target: /sandbox/dev
    type: dev

# Directory without content check
  - target: /sandbox/workspace
    type: dir

CLI equivalents:

  • --bind SRC:DEST → type: bind
  • --ro-bind SRC:DEST → type: ro-bind
  • --dev-bind SRC:DEST → type: dev-bind
  • --tmpfs TARGET [SIZE] → type: tmpfs with optional size
  • --proc TARGET → type: proc
  • --dev TARGET → type: dev
  • --dir TARGET → type: dir

Supported namespace types

Six namespaces can be unshared (isolated):

unshare:
  pid: true      # Isolate process tree
  uts: true      # Isolate hostname/domainname
  ipc: true      # Isolate inter-process communication
  cgroup: true   # Isolate cgroup hierarchy
  user: true     # Isolate user IDs
  network: true  # Isolate network stack (implies UTS and IPC)

CLI equivalents:

  • --unshare-pid
  • --unshare-uts
  • --unshare-ipc
  • --unshare-cgroup
  • --unshare-user
  • --unshare-net (also enables --unshare-uts and --unshare-ipc)

Example: Running an agent with specific capabilities

# Allow the agent to read from host directories but restrict system access
agent-nook run \
  --bind /home/enrico/data:/data \
  --ro-bind /var/lib/docker:/docker-data \
  --env DOCKER_DATA=/docker-data \
  --env HOME=/data/home \
  python3 my_agent.py

Example: Dropping all capabilities

agent-nook run --cap-drop ALL --chdir /workspace python3 agent.py

Example: Full isolation

agent-nook run \
  --cap-drop ALL \
  --unshare pid,ipc,uts,user \
  --chdir /sandbox \
  --env HOME=/sandbox/home \
  --env PATH=/usr/bin:/bin \
  python3 safe_agent.py

Technical Details

How we sandbox

  • Filesystem isolation: Agent's view of the filesystem is restricted to a subset of host paths via bind mounts.
  • Capability restrictions: Drops unnecessary Linux capabilities (e.g., SYS_ADMIN, SYS_PTRACE).
  • Network isolation: Isolates the network namespace to prevent access to host network resources. Note: bubblewrap does NOT filter network traffic; it only isolates the network stack.
  • No root needed: Works entirely in userspace using --unshare-pid, --unshare-uts, --unshare-ipc, --unshare-cgroup, --unshare-user, --unshare-net and --die-with-parent.

Why bubblewrap?

Bubblewrap is the same technology behind Flatpak. It's a single binary that applies sandbox rules without requiring a container runtime. It's available out of the box on any distribution supporting Flatpak packages.

Development

Prerequisites

  • Python 3.10 or newer
  • bubblewrap (install via dnf install bubblewrap, apt install bubblewrap, pip install bubblewrap-bin or download from the bubblewrap github releases)

Setting up development

git clone https://github.com/enricotagliavini/agent-nook
cd agent-nook
pip install -e ".[dev]"

# Run tests
./run_tests.sh

# Lint and format (run with --check to verify without fixing, as CI does)
./run_lint.sh

Continuous integration

A GitHub Actions workflow (.github/workflows/tests.yml) runs on every push and pull request:

  • test — full test suite (./run_tests.sh) across a Python 3.10–3.14 matrix on ubuntu-latest runners. Integration tests launch real sandboxes, so the job installs bubblewrap and lifts Ubuntu 24.04's default AppArmor restriction on unprivileged user namespaces (kernel.apparmor_restrict_unprivileged_userns=0).
  • lint — ./run_lint.sh --check (ruff lint + format verification).

License

GPLv3 — see LICENSE for details.

Contributing / Found a bug?

Contributions are welcome! Feel free to open an issue or a pull request.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.1.222 Sept 20266

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 22:45 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:b4d86e71163ec58986cd3f9a4b572f3a99482a570403e23938f29c9dbd25d2b5