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

tauri-permissions skill

/tauri/tauri-permissions

This skill guides you through configuring Tauri permissions, linking identifiers, scopes, and capabilities to enforce secure frontend access.

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

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

Files (1)
SKILL.md
9.4 KB
---
name: configuring-tauri-permissions
description: Guides the user through configuring Tauri permissions, including the security permission system, allow and deny lists, plugin permissions, permission identifiers, scopes, and capability integration.
---

# Tauri Permissions Configuration

This skill covers the Tauri v2 permission system for controlling frontend access to backend commands and system resources.

## Permission System Overview

Permissions in Tauri are explicit privileges that grant or deny access to specific commands. They form the security boundary between frontend code and system resources.

### Core Components

| Component | Purpose |
|-----------|---------|
| Permission | Defines access to specific commands |
| Scope | Restricts commands to specific paths/resources |
| Capability | Links permissions to windows/webviews |
| Identifier | Unique name referencing a permission |

### Security Model

- Frontend code cannot access commands without explicit permission
- Deny rules always take precedence over allow rules
- Permissions must be linked to capabilities to be active
- Each window/webview can have different permissions

## Permission Identifiers

### Naming Convention

Format: `<plugin-name>:<permission-type>`

| Pattern | Example | Description |
|---------|---------|-------------|
| `<name>:default` | `fs:default` | Default permission set |
| `<name>:allow-<command>` | `fs:allow-read-file` | Allow specific command |
| `<name>:deny-<command>` | `fs:deny-write-file` | Deny specific command |
| `<name>:allow-<scope>` | `fs:allow-app-read` | Allow with predefined scope |

### Identifier Rules

- Lowercase ASCII letters only: `[a-z]`
- Maximum length: 116 characters
- Plugin prefixes (`tauri-plugin-`) added automatically at compile time

## Directory Structure

### Application Structure

```
src-tauri/
├── capabilities/
│   ├── default.json          # Main capability file
│   └── admin.toml            # Additional capabilities
├── permissions/
│   └── custom-permission.toml # Custom app permissions
└── tauri.conf.json
```

### Plugin Structure

```
tauri-plugin-example/
├── permissions/
│   ├── default.toml          # Default permission set
│   ├── autogenerated/        # Auto-generated from commands
│   │   └── commands/
│   └── custom-scope.toml     # Custom scopes
└── src/
    ├── commands.rs
    └── build.rs
```

## Capability Configuration

Capabilities link permissions to windows and define what frontend contexts can access.

### JSON Format (Recommended for Apps)

```json
{
  "$schema": "../gen/schemas/desktop-schema.json",
  "identifier": "main-capability",
  "description": "Main window permissions",
  "windows": ["main"],
  "permissions": [
    "core:default",
    "fs:default",
    "fs:allow-read-text-file",
    {
      "identifier": "fs:allow-write-text-file",
      "allow": [{ "path": "$APPDATA/*" }]
    }
  ]
}
```

### TOML Format

```toml
"$schema" = "../gen/schemas/desktop-schema.json"
identifier = "main-capability"
description = "Main window permissions"
windows = ["main"]
permissions = [
  "core:default",
  "fs:default",
  "fs:allow-read-text-file"
]

[[permissions]]
identifier = "fs:allow-write-text-file"
allow = [{ path = "$APPDATA/*" }]
```

### Window Targeting

```json
{
  "identifier": "admin-capability",
  "windows": ["admin", "settings"],
  "permissions": ["fs:allow-write-all"]
}
```

Use `"*"` to target all windows:

```json
{
  "windows": ["*"],
  "permissions": ["core:default"]
}
```

### Platform-Specific Capabilities

```json
{
  "identifier": "desktop-capability",
  "platforms": ["linux", "macOS", "windows"],
  "windows": ["main"],
  "permissions": ["fs:allow-app-read-recursive"]
}
```

```json
{
  "identifier": "mobile-capability",
  "platforms": ["iOS", "android"],
  "windows": ["main"],
  "permissions": ["fs:allow-app-read"]
}
```

## Allow and Deny Lists

### Basic Scope Configuration

```json
{
  "identifier": "fs:allow-read-file",
  "allow": [
    { "path": "$HOME/Documents/*" },
    { "path": "$APPDATA/**" }
  ],
  "deny": [
    { "path": "$HOME/Documents/secrets/*" }
  ]
}
```

### Scope Variables

| Variable | Description |
|----------|-------------|
| `$APP` | Application install directory |
| `$APPCONFIG` | App config directory |
| `$APPDATA` | App data directory |
| `$APPLOCALDATA` | App local data directory |
| `$APPCACHE` | App cache directory |
| `$APPLOG` | App log directory |
| `$HOME` | User home directory |
| `$DESKTOP` | Desktop directory |
| `$DOCUMENT` | Documents directory |
| `$DOWNLOAD` | Downloads directory |
| `$RESOURCE` | App resource directory |
| `$TEMP` | Temporary directory |

### Glob Patterns

| Pattern | Matches |
|---------|---------|
| `*` | Any file in directory |
| `**` | Recursive (all subdirectories) |
| `*.txt` | Files with .txt extension |

### Deny Precedence

Deny rules always override allow rules:

```json
{
  "permissions": [
    {
      "identifier": "fs:allow-read-file",
      "allow": [{ "path": "$HOME/**" }],
      "deny": [{ "path": "$HOME/.ssh/**" }]
    }
  ]
}
```

## Plugin Permissions

### Using Default Plugin Permissions

```json
{
  "permissions": [
    "fs:default",
    "shell:default",
    "http:default",
    "dialog:default"
  ]
}
```

### Common Plugin Permission Patterns

#### Filesystem Plugin

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

#### HTTP Plugin

```json
{
  "permissions": [
    "http:default",
    {
      "identifier": "http:default",
      "allow": [{ "url": "https://api.example.com/*" }],
      "deny": [{ "url": "https://api.example.com/admin/*" }]
    }
  ]
}
```

#### Shell Plugin

```json
{
  "permissions": [
    "shell:allow-open",
    {
      "identifier": "shell:allow-execute",
      "allow": [
        { "name": "git", "cmd": "git", "args": true }
      ]
    }
  ]
}
```

### Directory-Specific Filesystem Permissions

| Permission | Access |
|------------|--------|
| `fs:allow-appdata-read` | Read $APPDATA (non-recursive) |
| `fs:allow-appdata-read-recursive` | Read $APPDATA (recursive) |
| `fs:allow-appdata-write` | Write $APPDATA (non-recursive) |
| `fs:allow-appdata-write-recursive` | Write $APPDATA (recursive) |
| `fs:allow-home-read-recursive` | Read $HOME (recursive) |
| `fs:allow-temp-write` | Write to temp directory |

## Custom Permission Definition

### TOML Permission File

Create `src-tauri/permissions/my-permission.toml`:

```toml
[[permission]]
identifier = "my-app:config-access"
description = "Access to app configuration files"
commands.allow = ["read_config", "write_config"]

[[scope.allow]]
path = "$APPCONFIG/*"

[[scope.deny]]
path = "$APPCONFIG/secrets.json"
```

### Permission Sets

Group multiple permissions:

```toml
[[set]]
identifier = "my-app:full-access"
description = "Full application access"
permissions = [
  "my-app:config-access",
  "fs:allow-app-read-recursive",
  "fs:allow-app-write-recursive"
]
```

### Auto-Generated Command Permissions

In plugin `src/build.rs`:

```rust
const COMMANDS: &[&str] = &["get_user", "save_user", "delete_user"];

fn main() {
    tauri_plugin::Builder::new(COMMANDS)
        .build();
}
```

This generates:
- `allow-get-user` / `deny-get-user`
- `allow-save-user` / `deny-save-user`
- `allow-delete-user` / `deny-delete-user`

### Default Permission Set

Create `permissions/default.toml`:

```toml
[default]
description = "Default permissions for my-plugin"
permissions = [
  "allow-get-user",
  "allow-save-user"
]
```

## Remote Access Configuration

Allow remote URLs to access Tauri APIs (use with caution):

```json
{
  "identifier": "remote-capability",
  "windows": ["main"],
  "remote": {
    "urls": ["https://*.myapp.com"]
  },
  "permissions": [
    "core:default"
  ]
}
```

**Security Warning**: Linux and Android cannot distinguish iframe requests from window requests.

## Configuration in tauri.conf.json

Reference capabilities by identifier:

```json
{
  "app": {
    "security": {
      "capabilities": ["main-capability", "admin-capability"]
    }
  }
}
```

Or inline capabilities directly:

```json
{
  "app": {
    "security": {
      "capabilities": [
        {
          "identifier": "inline-capability",
          "windows": ["*"],
          "permissions": ["core:default"]
        }
      ]
    }
  }
}
```

## Troubleshooting

### Common Errors

**"Not allowed on this command"**
- Verify command permission is in capability
- Check scope includes the target path
- Ensure capability targets correct window

**Permission not found**
- Check identifier spelling (lowercase only)
- Verify plugin is installed
- Run `cargo build` to regenerate permissions

### Debugging Permissions

1. Check generated schema: `src-tauri/gen/schemas/`
2. Review capability files load correctly
3. Verify window names match capability targets
4. Check deny rules are not blocking access

## Best Practices

1. **Principle of Least Privilege**: Only grant permissions actually needed
2. **Use Specific Scopes**: Prefer `$APPDATA/*` over `$HOME/**`
3. **Deny Sensitive Paths**: Always deny access to `.ssh`, credentials, etc.
4. **Separate Capabilities**: Use different capabilities for different window types
5. **Document Custom Permissions**: Include clear descriptions
6. **Review Plugin Defaults**: Understand what default permissions grant
7. **Platform-Specific Config**: Use platform targeting for OS-specific needs

Overview

This skill guides you through configuring Tauri v2 permissions to control frontend access to backend commands and system resources. It explains identifiers, scopes, allow/deny lists, capabilities, plugin permissions, and how to attach permissions to specific windows or platforms. The focus is practical setup, security rules, and troubleshooting guidance.

How this skill works

The skill inspects and explains the permission model used by Tauri: named permission identifiers, scope variables and glob patterns, allow and deny lists, and capability files that link permissions to windows or platforms. It shows how to define custom permission TOML/JSON files, auto-generate command permissions from plugins, and reference capabilities in tauri.conf.json so frontend code can call backend commands only when explicitly allowed.

When to use it

  • When you need to restrict frontend access to specific backend commands or file paths
  • When creating or reviewing capability files for different windows or platforms
  • When adding plugin permissions or generating command-specific permissions
  • When exposing remote URLs to Tauri APIs and needing a secure whitelist
  • When implementing least-privilege and auditing app security

Best practices

  • Follow principle of least privilege: grant only required permissions
  • Prefer narrow scopes (e.g., $APPDATA/*) over broad patterns like $HOME/**
  • Always add deny rules for sensitive locations (e.g., .ssh, credentials)
  • Separate capabilities per window type and target platforms explicitly
  • Document custom permission identifiers and descriptions for maintainability
  • Review plugin default permissions before enabling them

Example use cases

  • Main window capability that grants core and limited filesystem read access to app resources
  • Admin capability for settings windows with write permissions scoped to $APPDATA/*
  • Plugin that autogenerates allow/deny command identifiers from build.rs for fine-grained command control
  • Remote capability that allows specific subdomains to access Tauri APIs (use cautiously)
  • TOML permission set grouping multiple permissions into a single full-access identifier for internal tooling

FAQ

What format should capability files use, JSON or TOML?

Either is supported; JSON is commonly used for app-level capabilities and TOML is frequent for internal permission files. Choose the format consistent with your repo and tooling.

Why am I seeing "Not allowed on this command"?

Ensure the command's permission identifier is included in the active capability, the capability targets the correct window, and scope allow rules include the requested path while deny rules do not block it.