TLDR
Run Claude Code as a user. Use ACL to give claude access to specific folders, and to make sure you can read/write files created by claude.
Motivation
I want to let Claude run in yolo mode (--dangerously-skip-permissions), but I also want to make sure it’ll never delete my home directory by accident.
Alternatives
- Buy Claude a computer. I could let it run in a separate Mac mini1 2 or a cloud instance. This makes sense for tasks where Claude needs privileged access, but for most coding tasks it’s unnecessary overhead.
- Docker. Docker provides great isolation and probably matches the environment Claude is trained in, but it also adds friction to the workflow.
- Claude Code/Codex builtin sandbox. These things evolve with each version. It has some limitations and it’s hard to keep track of how it works. (Claude Code is not open source). I just want to run
--dangerously-skip-permissions!
Setup (tested on macOS)
1. Create the claude user
Use macOS System Settings → Users & Groups to create a new standard user named “claude”. You might need to login to the GUI as claude at least once for some things to work correctly (e.g. keychain).
2. Install Claude Code for the claude user
# Switch to claude user
su claude
# Install Claude Code
curl -fsSL https://claude.ai/install.sh | bash3. (Optional) Allow passwordless sudo to claude
This lets you switch to the claude user without typing a password each time:
echo "$(whoami) ALL=(claude) NOPASSWD: ALL" | sudo EDITOR='tee -a' visudo -f /etc/sudoers.d/claude4. Grant Claude access to a directory
# Set the directory you want to grant access to
DIR=/path/to/dir
# ACL permissions for full read/write/delete with inheritance
ACL_PERMS="allow read,write,list,add_file,search,add_subdirectory,delete_child,readattr,writeattr,readextattr,writeextattr,readsecurity,file_inherit,directory_inherit,delete"
# Grant claude access
chmod -R +a "user:claude $ACL_PERMS" "$DIR"
# Grant yourself access to files claude creates
chmod -R +a "user:$(whoami) $ACL_PERMS" "$DIR"To revoke access:
chmod -R -a "user:claude $ACL_PERMS" "$DIR"
chmod -R -a "user:$(whoami) $ACL_PERMS" "$DIR"This uses macOS ACLs (Access Control Lists) to grant the claude user access to specific folder, and to allow you to have r/w access to files created by claude.
Key ACL flags:
read,write: file content accesslist,add_file,search,add_subdirectory,delete_child,delete: directory operationsreadattr,writeattr,readextattr,writeextattr,readsecurity: metadata accessfile_inherit,directory_inherit: new files/subdirectories inherit this ACL
5. Run Claude Code as claude
sudo -u claude -i bash -lc "cd '$(pwd)' && claude --dangerously-skip-permissions"Automating it (Recommended)
The manual workflow above works, but you can automate it with a helper script.
Save this to ~/.local/bin/claude-access:
claude-access (click to expand)
#!/bin/bash
# Manage Claude's directory access
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/claude-access"
CONFIG_FILE="$CONFIG_DIR/directories"
CLAUDE_USER="claude"
mkdir -p "$CONFIG_DIR"
touch "$CONFIG_FILE"
usage() {
echo "Usage: claude-access <command> [args]"
echo ""
echo "Commands:"
echo " list List all shared directories"
echo " add <dir> Add a directory (use . for current)"
echo " remove <dir> Remove a directory (use . for current)"
echo " sync Apply permissions to all directories"
}
resolve_dir() {
local dir="$1"
if [[ "$dir" == "." ]]; then
pwd
else
realpath "$dir"
fi
}
list_dirs() {
if [[ ! -s "$CONFIG_FILE" ]]; then
echo "No directories configured."
else
cat "$CONFIG_FILE"
fi
}
apply_permissions() {
local dir="$1"
echo "Applying permissions: $dir"
# Grant claude access
chmod -R +a "user:$CLAUDE_USER allow read,write,list,add_file,search,add_subdirectory,delete_child,readattr,writeattr,readextattr,writeextattr,readsecurity,file_inherit,directory_inherit,delete" "$dir"
# Grant current user access to files claude creates
chmod -R +a "user:$(whoami) allow read,write,list,add_file,search,add_subdirectory,delete_child,readattr,writeattr,readextattr,writeextattr,readsecurity,file_inherit,directory_inherit,delete" "$dir"
}
remove_permissions() {
local dir="$1"
echo "Removing permissions: $dir"
chmod -R -a "user:$CLAUDE_USER allow read,write,list,add_file,search,add_subdirectory,delete_child,readattr,writeattr,readextattr,writeextattr,readsecurity,file_inherit,directory_inherit,delete" "$dir"
}
add_dir() {
local dir
dir=$(resolve_dir "$1")
if [[ ! -d "$dir" ]]; then
echo "Error: $dir is not a directory"
exit 1
fi
if grep -qxF "$dir" "$CONFIG_FILE" 2>/dev/null; then
echo "Already in list: $dir"
else
echo "$dir" >> "$CONFIG_FILE"
echo "Added: $dir"
apply_permissions "$dir"
fi
}
remove_dir() {
local dir
dir=$(resolve_dir "$1")
if grep -qxF "$dir" "$CONFIG_FILE" 2>/dev/null; then
grep -vxF "$dir" "$CONFIG_FILE" > "$CONFIG_FILE.tmp" && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
echo "Removed: $dir"
remove_permissions "$dir"
else
echo "Not in list: $dir"
fi
}
sync_dirs() {
if [[ ! -s "$CONFIG_FILE" ]]; then
echo "No directories configured. Use 'add' first."
exit 1
fi
while IFS= read -r dir; do
if [[ -d "$dir" ]]; then
apply_permissions "$dir"
else
echo "Skipping (not a directory): $dir"
fi
done < "$CONFIG_FILE"
echo "Done."
}
case "${1:-}" in
list)
list_dirs
;;
add)
[[ -z "${2:-}" ]] && { echo "Error: specify a directory"; exit 1; }
add_dir "$2"
;;
remove)
[[ -z "${2:-}" ]] && { echo "Error: specify a directory"; exit 1; }
remove_dir "$2"
;;
sync)
sync_dirs
;;
*)
usage
;;
esacMake it executable and ensure ~/.local/bin is in your PATH:
chmod +x ~/.local/bin/claude-access
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrcScript usage
claude-access add . # Grant access to current directory
claude-access remove . # Revoke access
claude-access list # List shared directories
claude-access sync # Re-apply permissions to all directories
# note: The directory list is stored at `~/.config/claude-access/directories`You can alias the run command in your shell config (e.g. .zshrc):
cc() {
local dir="$(pwd)"
claude-access add .
sudo -u claude -i bash -lc "cd '$dir' && claude --dangerously-skip-permissions $*"
}Usage
Once setup is complete (including the cc alias):
cd ~/path/to/your/project
ccPrinciple of least privilege3
In a typical Linux/macOS system, many services run as dedicated users such as sshd, postgres, _www. This ensures each service operates within controlled boundaries with limited filesystem access, reducing attack surface. Running Claude as its own user follows the same pattern. This could reduce the risk of prompt injection from debugging and pulling online texts. As agents (and harness) become more powerful, I can see them become daemons in the next year.
Example: Read-only mode
With filesystem-level permission control, you could grant read-only access by modifying the ACL permissions (dropping add_file, delete, writeattr, etc.). This would let you toggle read-only mode globally without per-tool logic.
However, Claude might not handle permission errors gracefully. It could retry failed writes or be confused. A <system-reminder> style message to inform the model it’s in read-only mode is probably needed.
Appendix
Dedicated group (optional)
If you’re on a machine with multiple users and want to prevent other users from reading files created by claude, you can create a group and make it the primary group of claude, so files created by claude are in group claude, and is only readable by claude (user) and you (via ACL).
Group creation commands (click to expand)
# Create a group just for claude
NEXT_GID=$(($(dscl . list /Groups PrimaryGroupID | awk '{print $2}' | sort -n | tail -1) + 1))
sudo dscl . create /Groups/claude
sudo dscl . create /Groups/claude PrimaryGroupID $NEXT_GID
sudo dscl . append /Groups/claude GroupMembership claude
sudo dscl . create /Users/claude PrimaryGroupID $NEXT_GIDGit Setup
The claude user needs git configuration (name, email) to make commits. Copy your .gitconfig or set it up manually.
GitHub Setup
By default, claude can’t use your gh token, unless you make the token available to claude. This is desirable for security. You can review the code before pushing manually, or go one step further to setup a separate GitHub account/token for Claude.
Alternative: Group permissions and umask
I also tried using group permission (g+rw) and umask to manage file access, but it’s not as robust as ACL.
Issues with umask (click to expand)
Setting umask for the claude user:
echo 'umask 002' | sudo tee -a /Users/claude/.bash_profileThis sets a default so files created by claude are group-writable.
Issues with this approach:
- Umask can be unreliable across different tools and contexts
- Claude Code’s
Writetool (as of v2.0.69) does not respect umask, meaning files created with theWritetool may not be accessible without manual permission fixes (sudo chown/chmod) - The ACL inheritance approach (shown above) is more reliable for ensuring you can access files claude creates
Troubleshooting
Troubleshooting commands (click to expand)
Check current ACLs on a directory
ls -le /path/to/dirVerify claude can access a directory
sudo -u claude ls /path/to/dirClear all ACLs from a directory
chmod -RN /path/to/dir