zudo-tauri

Type to search...

to open search from anywhere

Permissions and Capabilities

CreatedMar 29, 2026UpdatedMar 29, 2026Takeshi Takatsudo

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:

  1. Add the plugin to Cargo.toml
  2. Register it in main.rs with .plugin()
  3. 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:default is in the permissions list
  • Check that the window label matches the windows array in the capability file

”Plugin not found” or “Feature not available”

  • Check that the plugin is registered with .plugin() in main.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.urls array includes the dev server’s URL pattern
  • The pattern "http://localhost:*/**" covers any port on localhost

Key takeaways

  1. core:default is required — without it, no IPC commands work
  2. Each plugin needs its own permissiondialog:default, shell:default, etc.
  3. Remote URLs need explicit allowlisting — required for dev server IPC access
  4. Window labels must match — the capability’s windows array must include the window label
  5. No compile-time checks — missing permissions fail silently or at runtime
  6. Use strict CSP in production — avoid "csp": null outside of development