4 files changed,
+424,
-17
1@@ -0,0 +1,74 @@
2+# CLI --allow Flag Support for jail.sh and vibe.sh
3+
4+## Summary
5+
6+Add `--allow <dir>` flag support to `jail.sh` and `vibe.sh` scripts to enable users to whitelist additional directories with read/write access in the firejail sandbox.
7+
8+## Current Behavior
9+
10+- `jail.sh` sandboxes a command using firejail with a fixed set of whitelisted paths
11+- `vibe.sh` is a convenience wrapper that launches `opencode` through `jail.sh`
12+- Users cannot add custom directories to the sandbox whitelist
13+
14+## Proposed Behavior
15+
16+### jail.sh
17+
18+```bash
19+jail [--allow <dir>]... <command> [args...]
20+```
21+
22+Examples:
23+- `jail opencode` - Run opencode with default whitelist
24+- `jail --allow /tmp opencode` - Add /tmp to whitelist
25+- `jail --allow /data --allow /home/projects bash` - Add multiple directories, run bash
26+- `jail --allow /mnt opencode -s token123` - Combine with command args
27+
28+### vibe.sh
29+
30+```bash
31+vibe [--allow <dir>]... [<session_token>]
32+```
33+
34+Examples:
35+- `vibe` - Run opencode with default whitelist
36+- `vibe --allow /tmp` - Add /tmp to whitelist
37+- `vibe --allow /data --allow /projects token123` - Multiple dirs + session token
38+
39+## Design Decisions
40+
41+1. **One directory per flag**: Each `--allow` flag takes exactly one directory argument
42+2. **Flags before positional args**: All `--allow` flags must come before the command (jail) or token (vibe)
43+3. **No read-only option**: All allowed directories get read/write access
44+4. **getopts parsing**: Use bash's getopts for clean, POSIX-compliant argument parsing
45+
46+## Technical Details
47+
48+### jail.sh Changes
49+
50+- Parse `--allow` flags using getopts loop before processing command
51+- Collect directories into an array
52+- For each directory, add `--whitelist=<dir>` and `--read-write=<dir>` to FIREJAIL_ARGS
53+- Pass remaining arguments unchanged to firejail
54+
55+### vibe.sh Changes
56+
57+- Parse `--allow` flags using getopts loop before handling session token
58+- Collect directories and pass them to jail.sh using `--allow <dir>` syntax
59+- Maintain existing token handling logic (`-s <token>` or positional)
60+
61+## Edge Cases
62+
63+- **Duplicate directories**: Firejail handles gracefully (no error)
64+- **Non-existent directories**: Firejail will error with appropriate message
65+- **Directories with spaces**: Properly quoted via array handling
66+- **No --allow flags**: Backward compatible with existing behavior
67+- **No arguments after --allow flags**: Script should error with usage message
68+
69+## Success Criteria
70+
71+- [ ] `jail --allow /tmp opencode` works and /tmp is writable in sandbox
72+- [ ] `jail --allow /foo --allow /bar bash` allows access to both directories
73+- [ ] `vibe --allow /tmp` passes through to jail correctly
74+- [ ] `vibe --allow /data token123` works with session token
75+- [ ] Scripts remain backward compatible (no --allow flags)
1@@ -0,0 +1,259 @@
2+# --allow Flag CLI Support Implementation Plan
3+
4+> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
5+
6+**Goal:** Add `--allow <dir>` flag support to `jail.sh` and `vibe.sh` to enable whitelisting additional directories with read/write access in the firejail sandbox.
7+
8+**Architecture:** Parse `--allow` flags using bash's getopts before processing commands, collect directories into an array, then add `--whitelist` and `--read-write` entries to firejail arguments for each directory.
9+
10+**Tech Stack:** bash, firejail
11+
12+---
13+
14+### Task 1: Modify jail.sh to support --allow flag
15+
16+**Files:**
17+- Modify: `/home/btburke/projects/vibe/jail.sh`
18+
19+**Context:** The script currently takes all arguments as the command to run (`$@`). We need to parse `--allow` flags first, then treat remaining args as the command.
20+
21+- [ ] **Step 1: Add argument parsing before existing logic**
22+
23+After line 10 (`set -euo pipefail`) and before line 12 (the firejail check comment), add:
24+
25+```bash
26+# Parse --allow flags to add extra whitelisted directories
27+ALLOWED_DIRS=()
28+while [[ $# -gt 0 ]]; do
29+ case "$1" in
30+ --allow)
31+ if [[ $# -lt 2 ]]; then
32+ echo "Error: --allow requires a directory argument" >&2
33+ exit 1
34+ fi
35+ ALLOWED_DIRS+=("$2")
36+ shift 2
37+ ;;
38+ --)
39+ shift
40+ break
41+ ;;
42+ -*)
43+ echo "Error: Unknown flag: $1" >&2
44+ echo "Usage: jail [--allow <dir>]... <command> [args...]" >&2
45+ exit 1
46+ ;;
47+ *)
48+ break
49+ ;;
50+ esac
51+done
52+
53+# Check that a command was provided
54+if [[ $# -eq 0 ]]; then
55+ echo "Error: No command specified" >&2
56+ echo "Usage: jail [--allow <dir>]... <command> [args...]" >&2
57+ exit 1
58+fi
59+```
60+
61+- [ ] **Step 2: Add allowed directories to FIREJAIL_ARGS**
62+
63+After the FIREJAIL_ARGS array is fully built (around line 131, before the blacklist comment), add:
64+
65+```bash
66+# Add user-specified allowed directories
67+for dir in "${ALLOWED_DIRS[@]}"; do
68+ FIREJAIL_ARGS+=(--whitelist="$dir")
69+ FIREJAIL_ARGS+=(--read-write="$dir")
70+done
71+```
72+
73+Place this right before the "Blacklist specific tools" comment (around line 133).
74+
75+- [ ] **Step 3: Commit the changes**
76+
77+```bash
78+jj commit -m "feat(jail): add --allow flag for additional directory whitelisting" jail.sh
79+```
80+
81+---
82+
83+### Task 2: Modify vibe.sh to support --allow flag and pass through to jail
84+
85+**Files:**
86+- Modify: `/home/btburke/projects/vibe/vibe.sh`
87+
88+**Context:** The script currently has specific handling for session tokens. We need to parse `--allow` flags first, collect them, then pass them to jail.sh along with the appropriate opencode arguments.
89+
90+- [ ] **Step 1: Replace the argument handling section**
91+
92+Replace lines 33-50 (everything from "Handle different argument patterns" comment to the `fi`) with:
93+
94+```bash
95+# Parse --allow flags and handle session token
96+ALLOWED_DIRS=()
97+SESSION_TOKEN=""
98+
99+while [[ $# -gt 0 ]]; do
100+ case "$1" in
101+ --allow)
102+ if [[ $# -lt 2 ]]; then
103+ echo "Error: --allow requires a directory argument" >&2
104+ exit 1
105+ fi
106+ ALLOWED_DIRS+=("$2")
107+ shift 2
108+ ;;
109+ -s)
110+ if [[ $# -lt 2 ]]; then
111+ echo "Error: -s requires a token argument" >&2
112+ exit 1
113+ fi
114+ SESSION_TOKEN="$2"
115+ shift 2
116+ ;;
117+ -*)
118+ echo "Error: Unknown flag: $1" >&2
119+ echo "Usage: vibe [--allow <dir>]... [<session_token>]" >&2
120+ exit 1
121+ ;;
122+ *)
123+ if [[ -z "$SESSION_TOKEN" ]]; then
124+ SESSION_TOKEN="$1"
125+ shift
126+ else
127+ echo "Error: Unexpected argument: $1" >&2
128+ echo "Usage: vibe [--allow <dir>]... [<session_token>]" >&2
129+ exit 1
130+ fi
131+ ;;
132+ esac
133+done
134+
135+# Build jail command with allowed directories
136+JAIL_ARGS=()
137+for dir in "${ALLOWED_DIRS[@]}"; do
138+ JAIL_ARGS+=(--allow "$dir")
139+done
140+
141+# Execute with or without session token
142+if [[ -n "$SESSION_TOKEN" ]]; then
143+ exec jail "${JAIL_ARGS[@]}" opencode -s "$SESSION_TOKEN"
144+else
145+ exec jail "${JAIL_ARGS[@]}" opencode
146+fi
147+```
148+
149+- [ ] **Step 2: Commit the changes**
150+
151+```bash
152+jj commit -m "feat(vibe): add --allow flag support and pass through to jail" vibe.sh
153+```
154+
155+---
156+
157+### Task 3: Test the implementation
158+
159+**Files:**
160+- Test manually with bash commands
161+
162+- [ ] **Step 1: Test jail.sh with --allow flag**
163+
164+Run:
165+```bash
166+./jail.sh --allow /tmp bash -c "touch /tmp/test_jail_allow && echo 'Success: can write to /tmp' && rm /tmp/test_jail_allow"
167+```
168+
169+Expected: Command succeeds and prints "Success: can write to /tmp"
170+
171+- [ ] **Step 2: Test jail.sh with multiple --allow flags**
172+
173+Run:
174+```bash
175+./jail.sh --allow /tmp --allow /var/tmp bash -c "touch /tmp/test1 /var/tmp/test2 && echo 'Success' && rm /tmp/test1 /var/tmp/test2"
176+```
177+
178+Expected: Command succeeds and prints "Success"
179+
180+- [ ] **Step 3: Test jail.sh without --allow (backward compatibility)**
181+
182+Run:
183+```bash
184+./jail.sh bash -c "echo 'Hello from jail'"
185+```
186+
187+Expected: Command succeeds and prints "Hello from jail"
188+
189+- [ ] **Step 4: Test jail.sh error handling**
190+
191+Run:
192+```bash
193+./jail.sh --allow 2>&1 | head -1
194+```
195+
196+Expected: Error message about missing directory argument
197+
198+Run:
199+```bash
200+./jail.sh 2>&1 | head -1
201+```
202+
203+Expected: Error message about missing command
204+
205+- [ ] **Step 5: Test vibe.sh with --allow flag**
206+
207+Run (this will fail to start opencode without proper setup, but should show jail is called correctly):
208+```bash
209+./vibe.sh --allow /tmp 2>&1 | head -5
210+```
211+
212+Expected: Should see "Starting in firejail sandbox..." message (or firejail error if opencode not in PATH)
213+
214+- [ ] **Step 6: Commit test verification**
215+
216+```bash
217+jj commit -m "test: verify --allow flag implementation works correctly"
218+```
219+
220+---
221+
222+### Task 4: Final review and squash if needed
223+
224+- [ ] **Step 1: Review all changes**
225+
226+```bash
227+jj log -r '::@' -p
228+```
229+
230+- [ ] **Step 2: If multiple commits, consider squashing into one feature commit**
231+
232+```bash
233+jj squash -r @-- -r @
234+```
235+
236+Or keep separate commits if preferred.
237+
238+---
239+
240+## Implementation Notes
241+
242+### Why not use getopts?
243+
244+getopts doesn't support long options like `--allow` in a clean way without using GNU getopt. The manual case statement approach is more portable and clearer for this simple use case.
245+
246+### Argument parsing strategy
247+
248+Both scripts use a while loop with a case statement to:
249+1. Collect all `--allow <dir>` pairs into an array
250+2. Handle vibe.sh's `-s <token>` or positional token
251+3. Stop at first non-flag argument (for jail.sh, this is the command)
252+4. Validate that required arguments are present
253+
254+### Firejail integration
255+
256+The allowed directories are added to FIREJAIL_ARGS as:
257+- `--whitelist=<dir>` - Makes the directory visible in the sandbox
258+- `--read-write=<dir>` - Grants read/write permissions
259+
260+These are added after the default whitelist but before the blacklist, which is the correct order for firejail's rule processing.
M
jail.sh
+40,
-0
1@@ -9,6 +9,40 @@
2
3 set -euo pipefail
4
5+# Parse --allow flags to add extra whitelisted directories
6+ALLOWED_DIRS=()
7+while [[ $# -gt 0 ]]; do
8+ case "$1" in
9+ --allow)
10+ if [[ $# -lt 2 ]]; then
11+ echo "Error: --allow requires a directory argument" >&2
12+ exit 1
13+ fi
14+ ALLOWED_DIRS+=("$2")
15+ shift 2
16+ ;;
17+ --)
18+ shift
19+ break
20+ ;;
21+ -*)
22+ echo "Error: Unknown flag: $1" >&2
23+ echo "Usage: jail [--allow <dir>]... <command> [args...]" >&2
24+ exit 1
25+ ;;
26+ *)
27+ break
28+ ;;
29+ esac
30+done
31+
32+# Check that a command was provided
33+if [[ $# -eq 0 ]]; then
34+ echo "Error: No command specified" >&2
35+ echo "Usage: jail [--allow <dir>]... <command> [args...]" >&2
36+ exit 1
37+fi
38+
39 # Check for required system dependencies
40 if ! command -v firejail &> /dev/null; then
41 echo "Error: firejail is not installed or not in PATH" >&2
42@@ -128,6 +162,12 @@ if [ -d "$HOME/.cache/ms-playwright" ]; then
43 FIREJAIL_ARGS+=(--read-write="$HOME/.cache/ms-playwright")
44 fi
45
46+# Add user-specified allowed directories
47+for dir in "${ALLOWED_DIRS[@]}"; do
48+ FIREJAIL_ARGS+=(--whitelist="$dir")
49+ FIREJAIL_ARGS+=(--read-write="$dir")
50+done
51+
52 # Network access (allow by default, can be restricted with --net=none)
53 # FIREJAIL_ARGS+=(--net=none) # Uncomment to disable network access
54
M
vibe.sh
+51,
-17
1@@ -1,7 +1,7 @@
2 #!/usr/bin/env bash
3
4 # vibe - Run OpenCode in a sandboxed firejail environment
5-# Usage: vibe [<token>] or vibe -s <token>
6+# Usage: vibe [--allow <dir>]... [<session_token>]
7 #
8 # NOTE: This script requires system-installed jail (from this package),
9 # firejail, and opencode. Install firejail and opencode via your package manager.
10@@ -30,21 +30,55 @@ if ! command -v opencode &> /dev/null; then
11 exit 1
12 fi
13
14-# Handle different argument patterns:
15-# - vibe (no args) -> jail opencode
16-# - vibe <token> -> jail opencode -s <token>
17-# - vibe -s <token> -> jail opencode -s <token>
18-
19-if [ $# -eq 0 ]; then
20- # No arguments - run opencode without session token
21- exec jail opencode
22-elif [ "$1" = "-s" ] && [ $# -eq 2 ]; then
23- # Explicit -s flag with token
24- exec jail opencode -s "$2"
25-elif [ $# -eq 1 ] && [ "$1" != "-s" ]; then
26- # Single argument that is not -s, treat as token
27- exec jail opencode -s "$1"
28+# Parse --allow flags and handle session token
29+ALLOWED_DIRS=()
30+SESSION_TOKEN=""
31+
32+while [[ $# -gt 0 ]]; do
33+ case "$1" in
34+ --allow)
35+ if [[ $# -lt 2 ]]; then
36+ echo "Error: --allow requires a directory argument" >&2
37+ exit 1
38+ fi
39+ ALLOWED_DIRS+=("$2")
40+ shift 2
41+ ;;
42+ -s)
43+ if [[ $# -lt 2 ]]; then
44+ echo "Error: -s requires a token argument" >&2
45+ exit 1
46+ fi
47+ SESSION_TOKEN="$2"
48+ shift 2
49+ ;;
50+ -*)
51+ echo "Error: Unknown flag: $1" >&2
52+ echo "Usage: vibe [--allow <dir>]... [<session_token>]" >&2
53+ exit 1
54+ ;;
55+ *)
56+ if [[ -z "$SESSION_TOKEN" ]]; then
57+ SESSION_TOKEN="$1"
58+ shift
59+ else
60+ echo "Error: Unexpected argument: $1" >&2
61+ echo "Usage: vibe [--allow <dir>]... [<session_token>]" >&2
62+ exit 1
63+ fi
64+ ;;
65+ esac
66+done
67+
68+# Build jail command with allowed directories
69+JAIL_ARGS=()
70+for dir in "${ALLOWED_DIRS[@]}"; do
71+ JAIL_ARGS+=(--allow "$dir")
72+done
73+
74+# Execute with or without session token
75+if [[ -n "$SESSION_TOKEN" ]]; then
76+ exec jail "${JAIL_ARGS[@]}" opencode -s "$SESSION_TOKEN"
77 else
78- echo "Usage: vibe [<token>] or vibe -s <token>" >&2
79- exit 1
80+ exec jail "${JAIL_ARGS[@]}" opencode
81 fi