jail.sh
1#!/usr/bin/env bash
2
3# opencode-sandbox - Run OpenCode in a sandboxed firejail environment
4# This script runs in firejail with filesystem isolation
5# while maintaining access to current directory and configuration
6#
7# NOTE: This script requires system-installed firejail and opencode.
8# Install them via your package manager (e.g., pacman, apt, dnf).
9
10set -euo pipefail
11
12# Parse --allow flags to add extra whitelisted directories
13ALLOWED_DIRS=()
14while [[ $# -gt 0 ]]; do
15 case "$1" in
16 --allow)
17 if [[ $# -lt 2 ]]; then
18 echo "Error: --allow requires a directory argument" >&2
19 exit 1
20 fi
21 ALLOWED_DIRS+=("$2")
22 shift 2
23 ;;
24 --)
25 shift
26 break
27 ;;
28 -*)
29 echo "Error: Unknown flag: $1" >&2
30 echo "Usage: jail [--allow <dir>]... <command> [args...]" >&2
31 exit 1
32 ;;
33 *)
34 break
35 ;;
36 esac
37done
38
39# Check that a command was provided
40if [[ $# -eq 0 ]]; then
41 echo "Error: No command specified" >&2
42 echo "Usage: jail [--allow <dir>]... <command> [args...]" >&2
43 exit 1
44fi
45
46# Check for required system dependencies
47if ! command -v firejail &> /dev/null; then
48 echo "Error: firejail is not installed or not in PATH" >&2
49 echo "Please install firejail using your system package manager:" >&2
50 echo " Arch Linux: sudo pacman -S firejail" >&2
51 echo " Ubuntu/Debian: sudo apt install firejail" >&2
52 echo " Fedora: sudo dnf install firejail" >&2
53 exit 1
54fi
55
56if ! command -v opencode &> /dev/null; then
57 echo "Error: opencode is not installed or not in PATH" >&2
58 echo "Please install opencode using your system package manager" >&2
59 exit 1
60fi
61
62# Enable OpenCode experimental support for oxfmt when listed as a package dependency
63export OPENCODE_EXPERIMENTAL_OXFMT="true"
64export EDITOR="echo"
65
66# Capture current environment
67CURRENT_DIR="$(pwd)"
68ROOT="$(git rev-parse --show-toplevel)"
69
70# For NixOS: resolve to actual Nix store paths (not symlinks)
71OPENCODE_PATH="$(readlink -f "$(which opencode)")"
72echo "Using $OPENCODE_PATH"
73
74# Build firejail command arguments
75FIREJAIL_ARGS=(
76 # Use noprofile to avoid default restrictions that might block large directories
77 --noprofile
78
79 # Security restrictions
80 # NOTE: --private-tmp removed to allow opencode access to system /tmp
81 # for test scratch space and shared temporary files
82 --noroot # Disable su/sudo inside sandbox
83 --caps.drop=all # Drop all capabilities
84 --nonewprivs # Prevent privilege escalation
85 --nogroups # Don't inherit supplementary groups
86
87 # Filesystem access - whitelist specific paths only
88 --whitelist="$ROOT"
89 --read-only="$ROOT"
90 --whitelist="$ROOT/.git" # if started in a subdirectory of a git project
91 --read-write="$ROOT/.git" # => needs access to .git and .jj for bug and commits
92 --whitelist="$ROOT/.jj"
93 --read-write="$ROOT/.jj"
94 --whitelist="$CURRENT_DIR" # Allow access to current directory
95 --read-write="$CURRENT_DIR" # Make current directory writable
96 --whitelist="/nix/store" # Make Nix tools available in dev shell
97 --read-only="/nix/store"
98 --whitelist="$HOME/.bashrc"
99 --read-write="$HOME/.bashrc" # tool call aliases
100 --whitelist="$HOME/.agents"
101 --read-write="$HOME/.agents" # skills
102 --whitelist="$HOME/.ssh" # to troubleshoot Nix depoyment
103 --read-only="$HOME/.ssh"
104
105 # Terminal access - required for interactive TUI applications
106 --whitelist=/dev/tty # Allow access to controlling terminal
107 --read-write=/dev/tty
108
109 # Device access required for Bun/JavaScriptCore
110 --whitelist=/dev/shm # Shared memory required by JSC
111 --read-write=/dev/shm
112 --whitelist=/dev/urandom # Randomness required for crypto/timing
113 --read-only=/dev/urandom
114 --whitelist=/dev/random # Randomness fallback
115 --read-only=/dev/random
116 --whitelist=/dev/null # Standard I/O
117 --whitelist=/dev/zero # Memory allocation helpers
118 --whitelist=/dev/full
119 --whitelist=/dev/pts # Psuedo TTY
120)
121
122# Blacklist specific tools resident in /usr/bin environment that you absolutely don't want
123# OpenCode using. This augments any restrictions you put in your OpenCode permissions.
124BLACKLIST=(
125 npm
126 /usr/bin/npm
127 yarn
128 java
129 perl
130)
131for bl in "${BLACKLIST[@]}"; do
132 FIREJAIL_ARGS+=(--blacklist="$(which "$bl")")
133done
134
135# Add configuration access if files/directories exist
136if [ -d "$HOME/.config/opencode" ]; then
137 FIREJAIL_ARGS+=(--whitelist="$HOME/.config/opencode")
138 FIREJAIL_ARGS+=(--read-write="$HOME/.config/opencode")
139
140 # auth creds and logs
141 FIREJAIL_ARGS+=(--whitelist="$HOME/.local/share/opencode")
142 FIREJAIL_ARGS+=(--read-write="$HOME/.local/share/opencode")
143fi
144if [ -d "$HOME/.config/jj" ]; then
145 FIREJAIL_ARGS+=(--whitelist="$HOME/.config/jj")
146 FIREJAIL_ARGS+=(--read-only="$HOME/.config/jj")
147fi
148
149# Add PNPM store
150if [ -d "$HOME/.local/share/pnpm" ]; then
151 FIREJAIL_ARGS+=(--whitelist="$HOME/.config/pnpm")
152 FIREJAIL_ARGS+=(--read-only="$HOME/.config/pnpm")
153 FIREJAIL_ARGS+=(--whitelist="$HOME/.local/share/pnpm")
154 FIREJAIL_ARGS+=(--read-write="$HOME/.local/share/pnpm")
155 FIREJAIL_ARGS+=(--whitelist="$HOME/.cache/node") # corepack installs
156 FIREJAIL_ARGS+=(--read-only="$HOME/.cache/node")
157
158fi
159
160# add playwright browsers
161if [ -d "$HOME/.cache/ms-playwright" ]; then
162 FIREJAIL_ARGS+=(--whitelist="$HOME/.cache/ms-playwright")
163 FIREJAIL_ARGS+=(--read-write="$HOME/.cache/ms-playwright")
164fi
165
166# Add user-specified allowed directories
167for dir in "${ALLOWED_DIRS[@]}"; do
168 FIREJAIL_ARGS+=(--whitelist="$dir")
169 FIREJAIL_ARGS+=(--read-write="$dir")
170done
171
172# Network access (allow by default, can be restricted with --net=none)
173# FIREJAIL_ARGS+=(--net=none) # Uncomment to disable network access
174
175echo "Starting in firejail sandbox..."
176exec firejail "${FIREJAIL_ARGS[@]}" "$@"
177