Permissions and Capabilities
Configure Tauri v2 capabilities to control what the frontend can access, including plugins and remote URLs.
What are capabilities?
Tauri v2 uses a capabilities system to control what the frontend (webview) is allowed to do. Unlike Tauri v1, where all IPC commands were accessible by default, Tauri v2 requires explicit permission grants.
Capabilities are defined in JSON files under the capabilities/ directory in your Tauri project.
The default capability file
A typical capabilities/default.json:
{
"identifier": "default",
"windows": ["main"],
"remote": {
"urls": ["http://localhost:*/**"]
},
"permissions": ["core:default", "dialog:default", "shell:default"]
}
identifier
A unique name for this capability set. You can have multiple capability files for different security contexts.
windows
Which windows this capability applies to. ["main"] means only the window with label "main" gets these permissions. If you create additional windows (e.g., settings, about), you need to include their labels here or create separate capability files.
remote
Controls which remote URLs can access the Tauri IPC bridge. This is critical for development:
"remote": {
"urls": ["http://localhost:*/**"]
}
This allows any localhost URL (any port, any path) to invoke Tauri commands. Without this, the Vite dev server would not be able to communicate with the Rust backend.
📝 Note
In production, the frontend is loaded from tauri:// URLs, which always have IPC access. The remote section is primarily for development with a local dev server.
permissions
The list of permissions granted to the frontend. Each permission is scoped to a specific plugin or core feature.
Core permissions
core:default
This is the baseline permission that enables fundamental Tauri functionality:
- IPC command invocation (for commands registered in
generate_handler!) - Event system (
emit,listen) - Window management basics
Without core:default, the frontend cannot call any Tauri commands at all.
Plugin permissions
Each Tauri plugin defines its own permission scopes. You must explicitly grant them.
dialog:default
Enables native dialog APIs (file picker, folder picker, message boxes):
// Rust side: requires dialog plugin
app.dialog().file().blocking_pick_folder()
// Frontend side: requires dialog:default permission
import { open } from "@tauri-apps/plugin-dialog";
const selected = await open({ directory: true });
Requires the plugin to be registered:
tauri::Builder::default()
.plugin(tauri_plugin_dialog::init())
shell:default
Enables shell operations like opening URLs in the default browser:
// Rust side: requires shell plugin
tauri::Builder::default()
.plugin(tauri_plugin_shell::init())
// Frontend side: requires shell:default permission
import { open } from "@tauri-apps/plugin-shell";
await open("https://example.com");
⚠️ Warning
The shell:default permission allows the frontend to open URLs and execute shell commands. In security-sensitive applications, consider using more restrictive shell permissions instead of default.
Adding new permissions
When you add a new Tauri plugin, you must:
- Add the plugin to
Cargo.toml - Register it in
main.rswith.plugin() - Add its permission to
capabilities/default.json
For example, adding the clipboard plugin:
{
"permissions": [
"core:default",
"dialog:default",
"shell:default",
"clipboard-manager:default"
]
}
ℹ️ Info
If you add a plugin but forget to add its permission, frontend calls to that plugin’s API will fail silently or throw a permission error at runtime. There is no compile-time check.
Security: CSP configuration
Tauri v2 supports Content Security Policy (CSP) headers to restrict what the webview can load. In tauri.conf.json:
{
"app": {
"security": {
"csp": "default-src 'self'; script-src 'self'"
}
}
}
Setting CSP to null disables all CSP restrictions:
{
"app": {
"security": {
"csp": null
}
}
}
⚠️ Warning
Setting "csp": null disables Content Security Policy entirely. This means the webview can load scripts, styles, and resources from any origin. Only do this if you understand the security implications and your app does not handle sensitive data.
In development, a relaxed CSP is often necessary because the Vite dev server injects hot-reload scripts. In production, you should configure a strict CSP.
Multiple capability files
You can create separate capability files for different security contexts:
capabilities/
default.json # Main window permissions
settings.json # Settings window (restricted)
// capabilities/settings.json
{
"identifier": "settings",
"windows": ["settings"],
"permissions": ["core:default"]
}
This gives the settings window only core permissions, without access to dialog, shell, or other plugins.
Troubleshooting
”IPC call failed” or “Permission denied”
- Check that the command is listed in
generate_handler! - Check that
core:defaultis in the permissions list - Check that the window label matches the
windowsarray in the capability file
”Plugin not found” or “Feature not available”
- Check that the plugin is registered with
.plugin()inmain.rs - Check that the plugin’s permission is in
capabilities/default.json - Check that the plugin’s npm package is installed for frontend APIs
Dev server cannot call Tauri commands
- Check that the
remote.urlsarray includes the dev server’s URL pattern - The pattern
"http://localhost:*/**"covers any port on localhost
Key takeaways
core:defaultis required — without it, no IPC commands work- Each plugin needs its own permission —
dialog:default,shell:default, etc. - Remote URLs need explicit allowlisting — required for dev server IPC access
- Window labels must match — the capability’s
windowsarray must include the window label - No compile-time checks — missing permissions fail silently or at runtime
- Use strict CSP in production — avoid
"csp": nulloutside of development