{"id":31943481,"url":"https://github.com/dspearson/claude-confined","last_synced_at":"2025-10-14T09:57:04.345Z","repository":{"id":318565367,"uuid":"1071831846","full_name":"dspearson/claude-confined","owner":"dspearson","description":"claude-confined: take back control.","archived":false,"fork":false,"pushed_at":"2025-10-07T23:11:23.000Z","size":274,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-10-08T00:10:18.988Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dspearson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-07T21:52:43.000Z","updated_at":"2025-10-07T23:11:26.000Z","dependencies_parsed_at":"2025-10-08T00:10:35.099Z","dependency_job_id":"842b4a41-3871-4e24-82e2-df15445d0c75","html_url":"https://github.com/dspearson/claude-confined","commit_stats":null,"previous_names":["dspearson/claude-confined"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/dspearson/claude-confined","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dspearson%2Fclaude-confined","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dspearson%2Fclaude-confined/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dspearson%2Fclaude-confined/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dspearson%2Fclaude-confined/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dspearson","download_url":"https://codeload.github.com/dspearson/claude-confined/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dspearson%2Fclaude-confined/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279018554,"owners_count":26086405,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-14T02:00:06.444Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-14T09:57:02.126Z","updated_at":"2025-10-14T09:57:04.338Z","avatar_url":"https://github.com/dspearson.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Claude Confined\n\n\u003cimg src=\".claude-confined.png\" alt=\"Claude Confined Logo\"/\u003e\n\nTake back control.\n\n\u003c/div\u003e\n\n## Overview\n\nRun Claude Code in a sandboxed environment using bubblewrap, AppArmor, and seccomp filters to restrict filesystem access, prevent credential leakage, and limit system capabilities.\n\n**Security layers:**\n- **Bubblewrap** - User namespace containerization and filesystem isolation\n- **AppArmor** - Mandatory Access Control (MAC) with explicit deny rules for sensitive files\n- **Seccomp** - Berkeley Packet Filter (BPF) syscall filtering\n\n## Requirements\n\n### Required\n\n- **bubblewrap** (bwrap) - Containerization and namespace isolation\n  - Debian/Ubuntu: `sudo apt-get install bubblewrap`\n  - Fedora/RHEL: `sudo dnf install bubblewrap`\n  - Arch: `sudo pacman -S bubblewrap`\n\n- **AppArmor** - Mandatory Access Control\n  - Usually pre-installed on Ubuntu/Debian\n  - Check status: `sudo aa-status`\n  - Install: `sudo apt-get install apparmor apparmor-utils`\n\n- **gcc** and **libseccomp-dev** - For compiling seccomp filters\n  - Debian/Ubuntu: `sudo apt-get install gcc libseccomp-dev`\n  - Fedora/RHEL: `sudo dnf install gcc libseccomp-devel`\n  - Arch: `sudo pacman -S gcc libseccomp`\n\n### Auto-installed if missing\n\nThe installer will automatically install these if they're not found:\n\n- **bun** - JavaScript runtime (installed via official installer)\n- **Claude Code** - Installed via `bun install -g @anthropic-ai/claude-code`\n\n## Installation\n\n```bash\n# Basic installation (installs to ~/.local/bin)\nbin/install\n\n# Installation with permanently allowed directories\n# (adds directories to AppArmor profile for persistent access)\nbin/install --allow /home/user/projects --allow /workspace\n\n# View all installation options\nbin/install --help\n```\n\n**Important notes:**\n- Do NOT run with `sudo` - the installer uses sudo internally when needed\n- Installation requires sudo access for AppArmor profile deployment\n- The installer is interactive and checks all requirements\n- Bun and Claude Code will be installed automatically if missing\n\n**Post-installation:**\n\nEnsure `~/.local/bin` is in your PATH:\n\n```bash\nexport PATH=\"${HOME}/.local/bin:${PATH}\"\n```\n\nAdd this to your `~/.bashrc` or `~/.zshrc` to make it permanent.\n\n## Usage\n\n### Basic Usage\n\n```bash\n# Run Claude Code in sandboxed environment\nclaude-confined\n\n# Pass arguments to Claude normally\nclaude-confined chat\n\n# The current working directory is automatically accessible\ncd ~/my-project\nclaude-confined\n```\n\n### Runtime Directory Access\n\nAdd additional directories at runtime (temporary, not saved to AppArmor profile):\n\n```bash\n# Allow read-write access to additional paths\nclaude-confined --allow /path/to/project\n\n# Allow read-only access\nclaude-confined --ro-allow /path/to/docs\n\n# Multiple paths\nclaude-confined --allow /home/user/project1 --ro-allow /usr/share/docs\n\n# Combine with Claude arguments\nclaude-confined --allow /path/to/project chat\n```\n\n**Installation vs Runtime `--allow` flags:**\n\n- **Installation time** (`bin/install --allow PATH`): Permanently adds directory to AppArmor profile\n- **Runtime** (`claude-confined --allow PATH`): Temporarily grants access for that execution only\n\n### Debug Mode\n\nInspect the sandbox environment:\n\n```bash\n# Run arbitrary command inside sandbox\nclaude-confined --bash \"ls -la ~\"\nclaude-confined --bash \"env\"\nclaude-confined --bash \"cat /proc/self/status\"\n```\n\n### Environment Variables\n\n```bash\n# Skip permission checks (use with caution)\nDANGEROUSLY_SKIP_PERMISSIONS=1 claude-confined\n```\n\n## Emacs Integration\n\nUse `claude-confined` directly in Emacs with [`claude-code.el`](https://github.com/stevemolitor/claude-code.el):\n\n**For Doom Emacs:**\n```bash\n# Add doom-packages.el to ~/.config/doom/packages.el\n# Add doom-config.el to ~/.config/doom/config.el\ndoom sync\n```\n\n**For Vanilla Emacs:**\n```elisp\n;; Add to your Emacs init file\n(load-file \"/path/to/claude-confined/claude-code-emacs.el\")\n```\n\nFeatures:\n- **vterm backend** for fast terminal emulation\n- **monet integration** for LSP functionality\n- **Sandboxed execution** with all claude-confined security features\n- Helper functions to grant additional directory access\n\nSee [emacs/EMACS.md](emacs/EMACS.md) for complete installation and usage instructions.\n\n## Security Model\n\n### Filesystem Access\n\n**Allowed by default:**\n\n- **Current working directory** - Full read/write access\n- **`~/.claude/`** - Full read/write (Claude state, temporary files, logs)\n- **`~/.config/claude/`** - Read/write (Claude configuration)\n- **`~/.claude/ssh/`** - Mapped to `~/.ssh` inside sandbox (if directory exists)\n- **System binaries** - Read/execute only (`/usr`, `/bin`, `/lib`, etc.)\n- **Nix store** - Read/execute only (if `/nix` exists)\n- **Additional paths** - Via `--allow` or `--ro-allow` flags\n\n**Explicitly denied:**\n\n- **`~/.ssh/`** - SSH keys (host directory, use `~/.claude/ssh/` instead)\n- **`~/.gnupg/`** - GPG keys\n- **`~/.aws/`, `~/.docker/`, `~/.kube/`** - Cloud credentials\n- **`~/.bash_history`, `~/.zsh_history`** - Shell history files\n- **`~/.netrc`, `~/.pypirc`, `~/.npmrc`** - Package manager credentials\n- **`~/.cargo/credentials`** - Rust package credentials\n- **`/etc/shadow`, `/etc/sudoers`** - System sensitive files\n- **`/root/`** - Root user directory\n\n### Process Capabilities\n\n- **Cannot execute** `sudo`, `su`, `mount`, `umount`\n- **Cannot access** other users' files\n- **Runs in isolated namespaces** (UTS, IPC, user)\n- **No dangerous capabilities** inside namespace\n\n### Network Access\n\n- Network is **enabled** to allow Claude to communicate with Anthropic's API\n- Uses host network namespace (no network isolation)\n\n### Syscall Filtering\n\nSeccomp BPF filters block dangerous system calls.\n\n## Testing\n\nRun security tests to verify the sandbox:\n\n```bash\n# Using just (if installed)\njust test\n\n# Or run tests directly (requires claude-confined to be installed)\ncd ~/.claude/tmp \u0026\u0026 cp bin/run-tests . \u0026\u0026 claude-confined --bash \"perl run-tests\"\n```\n\nTests verify:\n- Sensitive files are blocked (`.gnupg/`, `.aws/`, `/etc/shadow`, etc.)\n- Allowed directories are accessible (`.claude/`, current directory)\n- Dangerous commands are blocked (`sudo`, `su`)\n\n## Monitoring and Auditing\n\nAppArmor logs all denied operations. Monitor logs to detect issues:\n\n```bash\n# View recent AppArmor denials\nsudo ausearch -m AVC -ts recent | grep claude-confined\n\n# Or check system journal\nsudo journalctl -xe | grep -i apparmor | grep claude-confined\n\n# Or check kernel logs\nsudo dmesg | tail -100 | grep -i apparmor\n```\n\n## Customisation\n\n### Modifying AppArmor Profile\n\nEdit the profile template and reinstall:\n\n```bash\n# Edit the profile\nvim apparmor/claude-confined\n\n# Reinstall to apply changes\nbin/install\n```\n\nThe profile uses three layers:\n1. **claude-confined** - Outer wrapper profile\n2. **claude-confined-bwrap** - Bubblewrap execution profile (with capabilities)\n3. **claude-confined-unpriv** - Unprivileged profile for processes inside namespace\n\n### Adding Permanent Directory Access\n\nAdd directories to the AppArmor profile permanently:\n\n```bash\nbin/install --allow /home/user/projects --allow /workspace\n```\n\nThis modifies the installed AppArmor profile to grant access to these directories.\n\n### Optional Profiles for Configuration Directories\n\nBy default, Claude is restricted from accessing configuration directories like `~/.config/emacs` or `~/.config/doom` to prevent sandbox escapes when integrated with editors. However, you can temporarily enable optional profiles when you specifically want Claude to help with configurations:\n\n```bash\n# List available optional profiles\njust list-profiles\n\n# Check which profiles are enabled\njust profile-status\n\n# Enable a profile (e.g., to let Claude edit your Emacs config)\njust enable-profile emacs-config\n\n# Work with Claude on your configuration\nclaude-confined\n\n# Disable when done\njust disable-profile emacs-config\n```\n\n**Available optional profiles:**\n- **`emacs-config`** - Grants access to Emacs and Doom configuration directories\n- **`browser-config`** - Grants access to Firefox/Chrome configuration\n- **`full-config-access`** - Grants access to all `~/.config` (use sparingly)\n\n**Best practices:**\n- Only enable profiles when you need them\n- Disable profiles immediately after completing your task\n- Use specific profiles (`emacs-config`) rather than broad ones (`full-config-access`)\n- Check `just profile-status` regularly to see what's enabled\n\nSee [apparmor/optional/README.md](apparmor/optional/README.md) for details on creating custom optional profiles.\n\n## Uninstallation\n\n```bash\n# Uninstall claude-confined\nbin/install --uninstall\n```\n\nThis removes:\n- Wrapper script (`~/.local/bin/claude-confined`)\n- Seccomp generator (`~/.local/lib/claude-confined/`)\n- AppArmor profile (`/etc/apparmor.d/claude-confined`)\n- Bun (only if installed by claude-confined installer)\n\n**Not removed:**\n- `~/.claude/` directory (your Claude data and SSH keys)\n- Claude Code (if installed separately or used by other tools)\n\n## Troubleshooting\n\n### Installation fails with \"sudo access required\"\n\nThe installer needs sudo to install the AppArmor profile. Ensure you have sudo privileges and try again.\n\n### \"Error: claude not found in PATH\"\n\nEnsure the installer completed successfully and `~/.local/bin` is in your PATH:\n\n```bash\nexport PATH=\"${HOME}/.local/bin:${PATH}\"\n```\n\n### \"Operation not permitted\" errors\n\n- Check AppArmor status: `sudo aa-status | grep claude-confined`\n- Review AppArmor logs: `sudo journalctl -xe | grep -i apparmor`\n- Ensure bubblewrap is installed: `which bwrap`\n\n### AppArmor profile not loading\n\n```bash\n# Check for syntax errors\nsudo apparmor_parser -r /etc/apparmor.d/claude-confined\n\n# View AppArmor logs\nsudo dmesg | grep -i apparmor\n```\n\n### Claude can't access files\n\n- Ensure you're running from the correct directory (CWD is automatically accessible)\n- Use `--allow /path/to/dir` to add additional directories at runtime\n- Check file permissions: `ls -la`\n- For permanent access, reinstall with `bin/install --allow /path/to/dir`\n\n## How It Works\n\n1. **Wrapper script** (`bin/claude-confined`) sets up the bubblewrap sandbox\n2. **Bubblewrap** creates isolated namespaces and bind-mounts filesystem\n3. **AppArmor** enforces MAC policy on all processes (three-layer profile)\n4. **Seccomp** filters dangerous syscalls (when enabled)\n5. **Claude Code** runs inside the sandbox with restricted access\n\nThe current directory is bind-mounted into the sandbox, allowing Claude to work on your project files while blocking access to sensitive home directory files.\n\n## Development\n\n### Using direnv\n\nThis project includes a `.envrc` file for [direnv](https://direnv.net/) that automatically displays available commands when you enter the directory:\n\n```bash\n# Install direnv (if not already installed)\nsudo apt-get install direnv\n\n# Add to your shell (~/.bashrc or ~/.zshrc):\neval \"$(direnv hook bash)\"  # for bash\neval \"$(direnv hook zsh)\"   # for zsh\n\n# Allow the .envrc in this directory:\ndirenv allow\n```\n\nNow whenever you `cd` into the claude-confined directory, you'll see the available `just` commands automatically.\n\n## Licence\n\nISC Licence - see [LICENCE](LICENCE) file for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdspearson%2Fclaude-confined","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdspearson%2Fclaude-confined","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdspearson%2Fclaude-confined/lists"}