Securing AI Coding Tools – How to Sandbox Devin Desktop with Bubblewrap

Allowing an AI coding assistant or autonomous developer agent (like Devin Desktop) to run local Bash scripts, use CLI tools, and access your local development environment comes with inherent security risks. By default, these applications run with your user permissions, meaning they have unrestricted access to your private SSH keys (~/.ssh), cloud credentials (~/.aws), GPG keys (~/.gnupg), chat histories, and sensitive private documents.

To mitigate this risk without crippling the app’s capability to code, test, and interact with your development sites, we can isolate it using Bubblewrap (bwrap)—the same lightweight containerization utility that powers Flatpak.

Below is the production-ready sandboxing script, a detailed breakdown of its configuration items, and the exact diagnostic toolkit used to troubleshoot and lock it down.

Tested Oct 2026 in Linux Mint 21.2 Cinnamon, Linux Kernel 6.8.0-138-generic on Devin Desktop Version: 3.10.67, VS Code OSS Version: 1.126.0, Electron: 42.2.0

The Complete devin-sandboxed.sh Script

save as ~/bin/devin-sandboxed.sh and then make it executable and change the System Menu (the Magic or Windows or Clover key opens it)

# save as ~/bin/devin-sandboxed.sh
# chmod +x ~/bin/devin-sandboxed.sh
# In Menu, right-click Edit Menu, select Devin (in Programming), click Properties, in Command put
#   /home/YOURNAME/bin/devin-sandboxed.sh %F
#   NOTE: do not use ~ instead of /home/YOURNAME 
#   /home/YOURNAME/bin/devin-sandboxed.sh %F
#   NOTE: can not use ~ instead of /home/YOURNAME
# To capture errors
#   strace -f -e trace=connect,accept,socket -o ~/Documents/devin_failures.txt ~/bin/devin-sandboxed.sh

The full contents of the script:

#!/bin/bash

# To capture errors for troubleshooting:
#   strace -f -e trace=connect,accept,socket -o ~/Documents/devin_failures.txt ~/bin/devin-sandboxed.sh

# Configuration: Define paths using explicit variables
ALLOWED_SITE_DIR="$HOME/sites"
CONFIG_DIR="$HOME/.config/devin-desktop"
DEVIN_PROPER_CONFIG="$HOME/.config/Devin"
LOCAL_SHARE="$HOME/.local/share/devin-desktop"
DEVIN_HOME_CONFIG="$HOME/.devin"
CACHE_DIR="$HOME/.cache/devin-desktop"

# Additional required tool/UI paths
VSCODE_DIR="$HOME/.vscode"
WINDSURF_DIR="$HOME/.windsurf"
CODEIUM_DIR="$HOME/.codeium"
AGENTS_DIR="$HOME/.agents"
LANDO_BIN="$HOME/.lando"
NPM_GLOBAL="$HOME/.npm-global"
FONT_CACHE="$HOME/.cache/fontconfig"
THEMES_DIR="$HOME/.themes"

# Ensure config/share dirs exist so they can be mounted writable
mkdir -p "$CONFIG_DIR" "$DEVIN_PROPER_CONFIG" "$LOCAL_SHARE" "$DEVIN_HOME_CONFIG" "$CACHE_DIR" "$VSCODE_DIR" "$WINDSURF_DIR" "$CODEIUM_DIR" "$AGENTS_DIR" "$ALLOWED_SITE_DIR"

# Build BWRAP flags array safely
BWRAP_ARGS=(
    --unshare-user
    --share-net
    --uid 1000
    --gid 1000
    --clearenv

    # Environment Setup for Display and User
    --setenv HOME "$HOME"
    --setenv USER "$USER"
    --setenv DISPLAY "$DISPLAY"
    --setenv WAYLAND_DISPLAY "$WAYLAND_DISPLAY"
    --setenv XDG_RUNTIME_DIR "$XDG_RUNTIME_DIR"
    --setenv DBUS_SESSION_BUS_ADDRESS "$DBUS_SESSION_BUS_ADDRESS"
    --ro-bind-try "$HOME/.Xauthority" "$HOME/.Xauthority"
    --bind-try "${XDG_RUNTIME_DIR}" "${XDG_RUNTIME_DIR}"

    # System core read-only binds
    --ro-bind /usr /usr
    --ro-bind /lib /lib
    --ro-bind /lib64 /lib64
    --ro-bind /etc /etc
    --ro-bind /sys /sys
    --bind /dev/shm /dev/shm
    --bind-try /dev/dri /dev/dri
    --symlink usr/bin /bin
    --proc /proc
    --dev /dev
    --tmpfs /tmp

    # Enable machine identity for D-Bus / keyring integration
    --ro-bind-try /var/lib/dbus /var/lib/dbus

    # Recreate a clean home directory structure inside the sandbox
    --dir "$HOME"

    # Explicitly bind your allowed site directory and app configurations
    --bind "$ALLOWED_SITE_DIR" "$ALLOWED_SITE_DIR"
    --bind "$CONFIG_DIR" "$CONFIG_DIR"
    --bind "$DEVIN_PROPER_CONFIG" "$DEVIN_PROPER_CONFIG"
    --bind "$LOCAL_SHARE" "$LOCAL_SHARE"
    --bind "$DEVIN_HOME_CONFIG" "$DEVIN_HOME_CONFIG"
    --bind "$CACHE_DIR" "$CACHE_DIR"
    --bind "$VSCODE_DIR" "$VSCODE_DIR"
    --bind "$WINDSURF_DIR" "$WINDSURF_DIR"
    --bind "$CODEIUM_DIR" "$CODEIUM_DIR"
    --bind "$AGENTS_DIR" "$AGENTS_DIR"

    # Bind Development Tools & System UI (Read-Only where appropriate)
    --bind "$LANDO_BIN" "$LANDO_BIN"
    --bind "$NPM_GLOBAL" "$NPM_GLOBAL"
    --ro-bind-try "$HOME/.config/git/config" "$HOME/.config/git/config"
    --ro-bind-try "$FONT_CACHE" "$FONT_CACHE"
    --ro-bind-try "$THEMES_DIR" "$THEMES_DIR"

    # Explicitly block/hide sensitive folders by turning them into empty tmpfs mounts
    --tmpfs "$HOME/.ssh"
    --tmpfs "$HOME/Documents"
    --tmpfs "$HOME/.gnupg"
    --tmpfs "$HOME/.aws"
    --tmpfs "$HOME/.claude"
    --tmpfs "$HOME/.bash_history"

    # GUI / X11 / Wayland socket access
    --ro-bind /tmp/.X11-unix /tmp/.X11-unix
    --bind-try "${XDG_RUNTIME_DIR}" "${XDG_RUNTIME_DIR}"
)

exec bwrap "${BWRAP_ARGS[@]}" /usr/bin/devin-desktop "$@"

Line-by-Line Breakdown: What Each Item Does and Fixes

1. Namespace & Core Isolation Flags

  • --unshare-user & --uid 1000 --gid 1000: Maps your current user into an unprivileged container namespace so it cannot escalate privileges or affect the host system.
  • --share-net: Essential for Electron-based apps and deep-link handlers. Without network sharing, local TCP loopback communication breaks, and OAuth callback redirection fails to reach the app.
  • --clearenv: Wipes out inherited host environment variables to ensure a pristine, controlled launch environment.

2. Graphics Rendering & Performance (/dev/shm, /dev/dri)

  • --bind /dev/shm /dev/shm: Chromium and Electron applications rely heavily on shared memory (/dev/shm) for inter-process communication (IPC) and rendering buffers. Without this, the UI renderer starves, resulting in blank or frozen “not responding” windows.
  • --bind-try /dev/dri /dev/dri: Passes through Direct Rendering Infrastructure nodes to enable hardware-accelerated GPU graphics pipelines.

3. Data Privacy & Hard Blocking (--tmpfs)

  • --tmpfs "$HOME/.ssh", --tmpfs "$HOME/.aws", etc.: Instead of just restricting permissions, these directives create empty, isolated RAM-backed filesystem views (tmpfs) over your private directories. To the application, these folders appear completely empty or non-existent, shielding your credentials from exposure.

4. Authentication & Keyring Integration (/var/lib/dbus)

  • --ro-bind-try /var/lib/dbus /var/lib/dbus: Electron and VS Code-based apps look for /var/lib/dbus/machine-id to identify the machine context. Binding this allows the app to cleanly initialize state tracking and fall back gracefully to local file-based credential storage when a full desktop secret service daemon is unavailable.

Troubleshooting Toolkit: How We Diagnosed and Fixed It

When building strict application sandboxes, things inevitably break on the first try. Here are the exact diagnostic utilities and grep filters used to track down hidden dependencies:

Capturing File Access Failures (strace)

To see every file path, library, or socket the application tried to open or touch during startup:

strace -f -e trace=openat,access -o ~/Documents/devin_failures.txt ~/bin/devin-sandboxed.sh

Filtering D-Bus, Sockets, and Machine IDs

To diagnose why desktop integration or authentication handlers were failing, we filtered the strace log using grep:

grep -i -E "dbus|bus|socket" ~/Documents/devin_failures.txt

What it revealed: Missing access to /var/lib/dbus/machine-id, which caused authentication modules to lock up until the read-only bind was added.

Comments

Leave a Reply