home / skills / dchuk / claude-code-tauri-skills / tauri-plugin-permissions

tauri-plugin-permissions skill

/tauri/tauri-plugin-permissions

This skill helps you configure Tauri plugin permissions and capabilities across windows and platforms, improving security with scoped access.

npx playbooks add skill dchuk/claude-code-tauri-skills --skill tauri-plugin-permissions

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
10.4 KB
---
name: managing-tauri-plugin-permissions
description: Guides users through configuring Tauri plugin permissions, capabilities, and security. Covers platform-specific capabilities, window-targeted permissions, using official and community plugin permissions, and writing custom plugin permissions with scopes.
---

# Managing Tauri Plugin Permissions

Tauri's capability and permission system provides granular security control for desktop and mobile applications. This skill covers configuring capabilities for windows and platforms, using plugin permissions effectively, and writing custom plugin permissions.

## Core Concepts

### Capabilities

Capabilities are JSON configuration files that assign permissions to specific windows and platforms. They follow the principle of least privilege.

**Location**: `src-tauri/capabilities/`

**Structure**:
```json
{
  "identifier": "capability-name",
  "description": "Human-readable purpose",
  "local": true,
  "windows": ["window-label"],
  "permissions": ["plugin:allow-action"],
  "platforms": ["linux", "windows", "macos", "android", "ios"]
}
```

### Permission Levels

Permissions operate at two levels:
- **Commands**: Individual operations (e.g., `allow-write-text-file`)
- **Scopes**: Path-based restrictions defining accessible files/directories

## Platform-Specific Capabilities

### Targeting Platforms

Restrict capabilities to specific operating systems using the `platforms` field:

```json
{
  "identifier": "desktop-fs-access",
  "description": "Filesystem access for desktop platforms",
  "windows": ["main"],
  "permissions": ["fs:allow-home-read"],
  "platforms": ["linux", "windows", "macos"]
}
```

Available platforms: `linux`, `windows`, `macos`, `android`, `ios`

### Mobile-Only Capabilities

```json
{
  "identifier": "mobile-camera",
  "description": "Camera access for mobile devices",
  "windows": ["main"],
  "permissions": ["camera:allow-capture"],
  "platforms": ["android", "ios"]
}
```

## Window-Specific Capabilities

### Configuring Multiple Windows

Define windows in `tauri.conf.json`:

```json
{
  "windows": [
    {
      "label": "main",
      "title": "Main Window",
      "width": 800,
      "height": 600
    },
    {
      "label": "settings",
      "title": "Settings",
      "width": 400,
      "height": 300
    }
  ]
}
```

### Assigning Capabilities to Windows

Create separate capability files for each window's needs:

**`src-tauri/capabilities/main-window.json`**:
```json
{
  "identifier": "main-window-capabilities",
  "description": "Full access for main window",
  "local": true,
  "windows": ["main"],
  "permissions": [
    "fs:allow-home-read",
    "fs:allow-home-write",
    "dialog:allow-open",
    "dialog:allow-save"
  ]
}
```

**`src-tauri/capabilities/settings-window.json`**:
```json
{
  "identifier": "settings-window-capabilities",
  "description": "Limited access for settings window",
  "local": true,
  "windows": ["settings"],
  "permissions": [
    "fs:allow-app-read",
    "fs:allow-app-write"
  ]
}
```

### Shared Capabilities Across Windows

```json
{
  "identifier": "shared-dialog",
  "description": "Dialog access for multiple windows",
  "local": true,
  "windows": ["main", "settings"],
  "permissions": ["dialog:allow-ask", "dialog:allow-message"]
}
```

## Using Plugin Permissions

### Default Permission Sets

Every plugin provides a `default` permission set with baseline access. Enable it with:

```json
{
  "permissions": ["plugin-name:default"]
}
```

### Finding Available Permissions

1. **Official plugins**: Check Tauri documentation's permission tables
2. **Plugin source**: Look in `permissions/autogenerated` directories
3. **Community plugins**: Check repository or crates.io page

### Permission Identifier Format

```
plugin-name:permission-name
```

Examples:
- `fs:allow-read`
- `fs:allow-write-text-file`
- `dialog:allow-open`
- `shell:allow-spawn`

### Configuring with Scopes

Restrict permissions to specific paths:

```json
{
  "identifier": "default",
  "description": "Main window capabilities",
  "windows": ["main"],
  "permissions": [
    "fs:allow-write-text-file",
    {
      "identifier": "fs:allow-read",
      "allow": [{ "path": "$HOME/Documents/**" }]
    },
    {
      "identifier": "fs:allow-write",
      "allow": [{ "path": "$APP/**" }]
    }
  ]
}
```

### Common Scope Variables

| Variable | Description |
|----------|-------------|
| `$APP` | Application data directory |
| `$HOME` | User home directory |
| `$RESOURCE` | Application resources |
| `$TEMP` | Temporary directory |
| `$DESKTOP` | User desktop |
| `$DOCUMENT` | User documents |
| `$DOWNLOAD` | User downloads |

### Deny Permissions

Explicitly deny specific operations:

```json
{
  "permissions": [
    "fs:default",
    "fs:deny-write-text-file"
  ]
}
```

## Writing Custom Plugin Permissions

### Plugin Structure

Create a plugin with the Tauri CLI:

```bash
cargo tauri plugin new my-plugin
cd tauri-plugin-my-plugin
```

### Implementing Commands

**`src/commands.rs`**:
```rust
use tauri::{command, AppHandle, Runtime};

#[command]
pub(crate) async fn read_data<R: Runtime>(
    key: String,
    app: AppHandle<R>,
) -> Result<String, String> {
    // Implementation
    Ok(format!("Data for key: {}", key))
}

#[command]
pub(crate) async fn write_data<R: Runtime>(
    key: String,
    value: String,
    app: AppHandle<R>,
) -> Result<(), String> {
    // Implementation
    Ok(())
}

#[command]
pub(crate) async fn delete_data<R: Runtime>(
    key: String,
    app: AppHandle<R>,
) -> Result<(), String> {
    // Implementation
    Ok(())
}
```

### Auto-Generating Permissions

**`src/build.rs`**:
```rust
const COMMANDS: &[&str] = &["read_data", "write_data", "delete_data"];

fn main() {
    tauri_plugin::Builder::new(COMMANDS)
        .global_api_script_path("./api-iife.js")
        .build();
}
```

This generates:
- `allow-read-data` / `deny-read-data`
- `allow-write-data` / `deny-write-data`
- `allow-delete-data` / `deny-delete-data`

### Defining Default Permissions

**`permissions/default.toml`**:
```toml
"$schema" = "schemas/schema.json"

[default]
description = "Default permissions for my-plugin. Allows read operations only."
permissions = ["allow-read-data"]
```

### Creating Permission Sets

**`permissions/read-write.toml`**:
```toml
"$schema" = "schemas/schema.json"

[[set]]
identifier = "read-write"
description = "Allows both read and write operations"
permissions = ["allow-read-data", "allow-write-data"]
```

**`permissions/full-access.toml`**:
```toml
"$schema" = "schemas/schema.json"

[[set]]
identifier = "full-access"
description = "Allows all operations including delete"
permissions = ["allow-read-data", "allow-write-data", "allow-delete-data"]
```

### Registering Commands

**`src/lib.rs`**:
```rust
use tauri::{
    plugin::{Builder, TauriPlugin},
    Manager, Runtime,
};

mod commands;

pub fn init<R: Runtime>() -> TauriPlugin<R> {
    Builder::new("my-plugin")
        .invoke_handler(tauri::generate_handler![
            commands::read_data,
            commands::write_data,
            commands::delete_data,
        ])
        .build()
}
```

### Frontend JavaScript Bindings

**`guest-js/index.ts`**:
```typescript
import { invoke } from '@tauri-apps/api/core';

export async function readData(key: string): Promise<string> {
  return await invoke('plugin:my-plugin|read_data', { key });
}

export async function writeData(key: string, value: string): Promise<void> {
  return await invoke('plugin:my-plugin|write_data', { key, value });
}

export async function deleteData(key: string): Promise<void> {
  return await invoke('plugin:my-plugin|delete_data', { key });
}
```

### Using Custom Plugin Permissions

In your application's capability file:

```json
{
  "identifier": "default",
  "windows": ["main"],
  "permissions": [
    "my-plugin:default",
    "my-plugin:read-write",
    "my-plugin:allow-delete-data"
  ]
}
```

## Complete Example: Cross-Platform App

**`src-tauri/capabilities/desktop.json`**:
```json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "desktop",
  "windows": ["main"],
  "platforms": ["linux", "windows", "macos"],
  "permissions": [
    "core:default",
    "fs:default",
    { "identifier": "fs:allow-read", "allow": [{ "path": "$HOME/Documents/**" }] },
    { "identifier": "fs:allow-write", "allow": [{ "path": "$APP/**" }] },
    "dialog:allow-open",
    "dialog:allow-save",
    "shell:allow-open"
  ]
}
```

**`src-tauri/capabilities/mobile.json`**:
```json
{
  "identifier": "mobile",
  "windows": ["main"],
  "platforms": ["android", "ios"],
  "permissions": [
    "fs:allow-app-read",
    "fs:allow-app-write",
    "notification:default"
  ]
}
```

## Best Practices

### Security

1. **Principle of least privilege**: Grant only required permissions
2. **Use scopes**: Restrict file access to specific paths rather than broad permissions
3. **Separate by window**: Each window should have only the permissions it needs
4. **Platform targeting**: Avoid granting mobile permissions on desktop and vice versa

### Organization

1. **Organize by function**: Group capabilities by feature area
2. **Use descriptive identifiers**: Make capability purposes clear
3. **Document permissions**: Include descriptions explaining why each permission is needed

### Plugin Development

1. **Minimal defaults**: Default permission sets should be restrictive
2. **Create permission sets**: Offer tiered access levels (read-only, read-write, full)
3. **Use auto-generation**: Let Tauri generate allow/deny permissions for commands
4. **Test permissions**: Verify permission behavior with example applications

## Troubleshooting

### Permission Denied Errors

If you encounter permission errors:

1. Check capability file syntax (valid JSON)
2. Verify the window label matches your configuration
3. Confirm the permission identifier is correct
4. Check if a scope is required for path-based operations

### Capability Not Applied

1. Ensure capability files are in `src-tauri/capabilities/`
2. Verify the `windows` array contains the correct window labels
3. Check `platforms` includes your target OS
4. Rebuild the application after capability changes

## References

- [Tauri Capabilities Documentation](https://v2.tauri.app/learn/security/capabilities-for-windows-and-platforms)
- [Using Plugin Permissions](https://v2.tauri.app/learn/security/using-plugin-permissions)
- [Writing Plugin Permissions](https://v2.tauri.app/learn/security/writing-plugin-permissions)
- [Capability Reference](https://v2.tauri.app/reference/acl/capability/)

Overview

This skill guides configuring Tauri plugin permissions, capabilities, and security for Tauri v2 apps. It explains how to target platforms and windows, use official and community plugin permission sets, and author custom plugin permissions with scopes. The goal is to help you apply least-privilege access and organize capability files for predictable behavior.

How this skill works

It inspects and describes the capability JSON structure placed in src-tauri/capabilities, showing how to assign permissions to windows and platforms. It covers permission levels (commands vs scopes), how to enable plugin default sets, how to restrict access with path scopes and variables, and how to generate and register custom plugin permissions. Troubleshooting tips and best practices help you validate and apply capability changes.

When to use it

  • When you need to restrict plugin or API access per window or platform
  • When building cross-platform desktop and mobile Tauri apps
  • When adding third-party or community plugins and you want explicit permission control
  • When creating a custom plugin and you must expose safe, scoped commands
  • When auditing app security to enforce least-privilege policies

Best practices

  • Follow principle of least privilege: grant only needed permissions
  • Use scopes to limit filesystem access to defined paths and variables (e.g. $HOME, $APP)
  • Separate capabilities by window so UI surfaces only get necessary access
  • Target capabilities by platform to avoid granting mobile privileges on desktop and vice versa
  • Provide descriptive identifiers and document why each permission is required

Example use cases

  • Desktop app: give main window filesystem and dialog access, limit settings window to app-only files
  • Mobile app: enable camera and notification permissions only on android and ios capability files
  • Plugin adoption: enable plugin-name:default in capability to get baseline access, then add scoped permissions as needed
  • Custom plugin: auto-generate allow/deny commands for read/write/delete and expose tiered permission sets (default, read-write, full-access)
  • Cross-platform distribution: keep desktop.json and mobile.json capability files to separate platform permissions

FAQ

How do I restrict a plugin permission to only a folder?

Use a scope object for the permission with allow paths, e.g. { "identifier":"fs:allow-read", "allow":[{"path":"$HOME/Documents/**"}] }.

Why is a capability not applied after changes?

Ensure the file is in src-tauri/capabilities, the windows array matches your window labels, platforms include your OS, and rebuild the app after changes.