2026-03-23 14:20:40 +01:00
[< Back to README ](../README.md )
# Configuration
## MCP Configuration
Model Context Protocol settings are stored in:
```
~/.config/Claude/claude_desktop_config.json
```
## Environment Variables
| Variable | Default | Description |
|----------|---------|-------------|
| `CLAUDE_USE_WAYLAND` | unset | Set to `1` to use native Wayland instead of XWayland. Note: Global hotkeys won't work in native Wayland mode. |
| `CLAUDE_MENU_BAR` | unset (`auto` ) | Controls menu bar behavior: `auto` (hidden, Alt toggles), `visible` / `1` (always shown), `hidden` / `0` (always hidden, Alt disabled). See [Menu Bar ](#menu-bar ) below. |
### Wayland Support
By default, Claude Desktop uses X11 mode (via XWayland) on Wayland sessions to ensure global hotkeys work. If you prefer native Wayland and don't need global hotkeys:
```bash
# One-time launch
CLAUDE_USE_WAYLAND=1 claude-desktop
# Or add to your environment permanently
export CLAUDE_USE_WAYLAND=1
```
**Important:** Native Wayland mode doesn't support global hotkeys due to Electron/Chromium limitations with XDG GlobalShortcuts Portal. If global hotkeys (Ctrl+Alt+Space) are important to your workflow, keep the default X11 mode.
### Menu Bar
By default, the menu bar is hidden but can be toggled with the Alt key (`auto` mode). On KDE Plasma and other DEs where Alt is heavily used, this can cause layout shifts. Use `CLAUDE_MENU_BAR` to control the behavior:
| Value | Menu visible | Alt toggles | Use case |
|-------|-------------|-------------|----------|
| unset / `auto` | No | Yes | Default — hidden, Alt toggles |
| `visible` / `1` / `true` / `yes` / `on` | Yes | No | Stable layout, no shift on Alt |
| `hidden` / `0` / `false` / `no` / `off` | No | No | Menu fully disabled, Alt free |
```bash
# Always show the menu bar (no layout shift on Alt)
CLAUDE_MENU_BAR=visible claude-desktop
# Or add to your environment permanently
export CLAUDE_MENU_BAR=visible
```
## Cowork Sandbox Mounts
When using Cowork mode with the BubbleWrap (bwrap) backend, you can customize
the sandbox mount points via `~/.config/Claude/claude_desktop_linux_config.json`
(a dedicated config for the Linux port, separate from the official
`claude_desktop_config.json` ):
```json
{
"preferences": {
"coworkBwrapMounts": {
"additionalROBinds": ["/opt/my-tools", "/nix/store"],
"additionalBinds": ["/home/user/shared-data"],
"disabledDefaultBinds": ["/etc"]
}
}
}
```
| Key | Type | Description |
|-----|------|-------------|
feat(bwrap): support {src, dst} mount form in coworkBwrapMounts (#531)
* feat(bwrap): support {src, dst} mount form for distinct host/sandbox paths
Extends coworkBwrapMounts (#339) so additionalROBinds and additionalBinds
accept entries of the form { src, dst } in addition to the existing string
form. This unlocks the persistent /tmp use case: the default --tmpfs /tmp
gets wiped between Bash tool calls because of --die-with-parent, and the
old string-only API (--bind p p) had no way to map a host directory under
$HOME onto /tmp inside the sandbox without exposing the host /tmp itself.
Validation:
- src: same checks as the string form (absolute, not in
FORBIDDEN_MOUNT_PATHS, $HOME constraint when RW)
- dst: absolute and non-forbidden only — the $HOME constraint is
intentionally skipped since the whole point of the form is to map
outside $HOME (e.g. /tmp)
- malformed objects are filtered out with a warning, matching the
existing string-validation behavior
Doctor (--doctor) renders the object form as "src -> dst" in both the
Python and Node parser branches.
100% backwards compatible: the string form is preserved unchanged. The 36
existing tests pass; 13 new tests cover accept/reject paths, mixed
string+object configs, the persistent-/tmp recipe end-to-end, and the
doctor rendering (58/58 total).
Closes #530
---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>
* docs(configuration): document {src, dst} mount form
Refs #530
---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>
* chore(bwrap): address PR #531 review feedback
- doctor: warn when an additional mount's dst lands on a default RO
mount (/usr, /etc, /bin, /sbin, /lib, /lib64, or subpaths). bwrap
honors the later mount, so the user's bind silently replaces the
default — a config footgun, not an escape, but worth surfacing
(RayCharlizard issue 1)
- docs(configuration): note the shadowing implication under
"Distinct host/sandbox paths" (RayCharlizard issue 2)
- test(bwrap-config): pin the reject contract for dst under a
forbidden path (e.g. /proc/self), beyond the existing exact-match
case (RayCharlizard issue 3)
- bwrap-config: harmonize the rejected-mount warning text — the
string-form path now reads "rejected mount" like the object-form
variants (RayCharlizard issue 4)
Tests: 61/61 passing (3 new: 1 reject-subpath + 2 doctor shadow
positive/negative).
Refs #530
---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
2026-04-30 16:34:20 +02:00
| `additionalROBinds` | `(string \| {src, dst})[]` | Extra paths mounted read-only inside the sandbox. Accepts any absolute path except `/` , `/proc` , `/dev` , `/sys` . |
| `additionalBinds` | `(string \| {src, dst})[]` | Extra paths mounted read-write inside the sandbox. * * `src` is restricted to paths under `$HOME` ** for security; `dst` is unconstrained. |
2026-03-23 14:20:40 +01:00
| `disabledDefaultBinds` | `string[]` | Default mounts to skip. Cannot disable critical mounts (`/` , `/dev` , `/proc` ). Use with caution: disabling `/usr` or `/etc` may break tools inside the sandbox. |
feat(bwrap): support {src, dst} mount form in coworkBwrapMounts (#531)
* feat(bwrap): support {src, dst} mount form for distinct host/sandbox paths
Extends coworkBwrapMounts (#339) so additionalROBinds and additionalBinds
accept entries of the form { src, dst } in addition to the existing string
form. This unlocks the persistent /tmp use case: the default --tmpfs /tmp
gets wiped between Bash tool calls because of --die-with-parent, and the
old string-only API (--bind p p) had no way to map a host directory under
$HOME onto /tmp inside the sandbox without exposing the host /tmp itself.
Validation:
- src: same checks as the string form (absolute, not in
FORBIDDEN_MOUNT_PATHS, $HOME constraint when RW)
- dst: absolute and non-forbidden only — the $HOME constraint is
intentionally skipped since the whole point of the form is to map
outside $HOME (e.g. /tmp)
- malformed objects are filtered out with a warning, matching the
existing string-validation behavior
Doctor (--doctor) renders the object form as "src -> dst" in both the
Python and Node parser branches.
100% backwards compatible: the string form is preserved unchanged. The 36
existing tests pass; 13 new tests cover accept/reject paths, mixed
string+object configs, the persistent-/tmp recipe end-to-end, and the
doctor rendering (58/58 total).
Closes #530
---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>
* docs(configuration): document {src, dst} mount form
Refs #530
---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>
* chore(bwrap): address PR #531 review feedback
- doctor: warn when an additional mount's dst lands on a default RO
mount (/usr, /etc, /bin, /sbin, /lib, /lib64, or subpaths). bwrap
honors the later mount, so the user's bind silently replaces the
default — a config footgun, not an escape, but worth surfacing
(RayCharlizard issue 1)
- docs(configuration): note the shadowing implication under
"Distinct host/sandbox paths" (RayCharlizard issue 2)
- test(bwrap-config): pin the reject contract for dst under a
forbidden path (e.g. /proc/self), beyond the existing exact-match
case (RayCharlizard issue 3)
- bwrap-config: harmonize the rejected-mount warning text — the
string-form path now reads "rejected mount" like the object-form
variants (RayCharlizard issue 4)
Tests: 61/61 passing (3 new: 1 reject-subpath + 2 doctor shadow
positive/negative).
Refs #530
---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
2026-04-30 16:34:20 +02:00
### Distinct host/sandbox paths (`{src, dst}` form)
By default a string entry like `"/opt/tools"` mounts the host path at the
*same* path inside the sandbox. To map a host directory to a different path
inside the sandbox, use the object form `{ "src": "...", "dst": "..." }` .
The most common use case is making `/tmp` persistent across Bash tool calls.
Each Bash invocation spawns a fresh `bwrap` with `--tmpfs /tmp` and
`--die-with-parent` , so the default `/tmp` is wiped between calls. Mapping a
host cache directory onto `/tmp` keeps state across calls without exposing the
host's real `/tmp` :
```json
{
"preferences": {
"coworkBwrapMounts": {
"additionalBinds": [
{ "src": "/home/user/.cache/claude-tmp", "dst": "/tmp" }
],
"disabledDefaultBinds": ["/tmp"]
}
}
}
```
`disabledDefaultBinds: ["/tmp"]` is required to remove the default
`--tmpfs /tmp` so the bind takes effect.
The string and object forms can be mixed freely in the same array.
> **Caution:** Mapping `dst` onto a default RO mount (`/usr`, `/etc`, `/bin`,
> `/sbin`, `/lib`, `/lib64`) silently replaces it inside the sandbox; you
> almost never want this, and `--doctor` will warn if you do.
2026-03-23 14:20:40 +01:00
### Security notes
- Paths `/` , `/proc` , `/dev` , `/sys` (and their subpaths) are always rejected
feat(bwrap): support {src, dst} mount form in coworkBwrapMounts (#531)
* feat(bwrap): support {src, dst} mount form for distinct host/sandbox paths
Extends coworkBwrapMounts (#339) so additionalROBinds and additionalBinds
accept entries of the form { src, dst } in addition to the existing string
form. This unlocks the persistent /tmp use case: the default --tmpfs /tmp
gets wiped between Bash tool calls because of --die-with-parent, and the
old string-only API (--bind p p) had no way to map a host directory under
$HOME onto /tmp inside the sandbox without exposing the host /tmp itself.
Validation:
- src: same checks as the string form (absolute, not in
FORBIDDEN_MOUNT_PATHS, $HOME constraint when RW)
- dst: absolute and non-forbidden only — the $HOME constraint is
intentionally skipped since the whole point of the form is to map
outside $HOME (e.g. /tmp)
- malformed objects are filtered out with a warning, matching the
existing string-validation behavior
Doctor (--doctor) renders the object form as "src -> dst" in both the
Python and Node parser branches.
100% backwards compatible: the string form is preserved unchanged. The 36
existing tests pass; 13 new tests cover accept/reject paths, mixed
string+object configs, the persistent-/tmp recipe end-to-end, and the
doctor rendering (58/58 total).
Closes #530
---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>
* docs(configuration): document {src, dst} mount form
Refs #530
---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>
* chore(bwrap): address PR #531 review feedback
- doctor: warn when an additional mount's dst lands on a default RO
mount (/usr, /etc, /bin, /sbin, /lib, /lib64, or subpaths). bwrap
honors the later mount, so the user's bind silently replaces the
default — a config footgun, not an escape, but worth surfacing
(RayCharlizard issue 1)
- docs(configuration): note the shadowing implication under
"Distinct host/sandbox paths" (RayCharlizard issue 2)
- test(bwrap-config): pin the reject contract for dst under a
forbidden path (e.g. /proc/self), beyond the existing exact-match
case (RayCharlizard issue 3)
- bwrap-config: harmonize the rejected-mount warning text — the
string-form path now reads "rejected mount" like the object-form
variants (RayCharlizard issue 4)
Tests: 61/61 passing (3 new: 1 reject-subpath + 2 doctor shadow
positive/negative).
Refs #530
---
Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <claude@anthropic.com>
---------
Co-authored-by: Claude <claude@anthropic.com>
2026-04-30 16:34:20 +02:00
for both `src` and `dst`
- For read-write mounts (`additionalBinds` ), `src` must be under your home
directory. `dst` has no `$HOME` constraint — that is the entire purpose of
the object form (e.g. mapping onto `/tmp` )
2026-03-23 14:20:40 +01:00
- The core sandbox structure (`--tmpfs /` , `--unshare-pid` , `--die-with-parent` ,
`--new-session` ) cannot be modified
- Mount order is enforced: user mounts cannot override security-critical
read-only mounts
### Applying changes
The daemon reads the configuration at startup. After editing the config file,
restart the daemon:
```bash
pkill -f cowork-vm-service
```
The daemon will be automatically relaunched on the next Cowork session.
### Diagnostics
Run `claude-desktop --doctor` to see your custom mount configuration and any
warnings about potentially dangerous settings.
## Application Logs
Runtime logs are available at:
```
~/.cache/claude-desktop-debian/launcher.log
```