Docs/Permission System
Universal Developer

Permission System

Permission System

Overview

The plugin permission system controls access to sensitive resources and APIs. Starting from SDK API version 251212, all permission declarations are enforced.

Introduction

Permissions follow a least-privilege model and pair permissions with permissionReasons to explain intent to users.

Quick Start

1. Declare Permissions in manifest.json

EXAMPLE.JSON
{
  "id": "com.example.plugin",
  "name": "My Plugin",
  "version": "1.0.0",
  "sdkapi": 251212,
  "permissions": {
    "required": ["clipboard.read", "network.internet"],
    "optional": ["storage.shared"]
  },
  "permissionReasons": {
    "clipboard.read": "Read clipboard to get text for translation",
    "network.internet": "Access translation API services"
  }
}

2. Permission Categories

CategoryPermission IDRisk LevelDescription
Filesystemfs.readMediumRead user files
fs.writeHighWrite/modify files
fs.executeHighExecute files or scripts
Clipboardclipboard.readMediumRead clipboard content
clipboard.writeLowWrite to clipboard
Networknetwork.localLowAccess local network
network.internetMediumAccess internet
network.downloadMediumDownload files
Systemsystem.shellHighExecute system commands
system.notificationLowSend system notifications
system.trayMediumOperate system tray
Intelligenceintelligence.basicLowBasic intelligence capabilities
intelligence.adminHighAdmin intelligence capabilities
intelligence.agentsHighAgent system
Storagestorage.pluginLowPlugin private storage (auto-granted)
storage.sharedMediumCross-plugin shared storage
storage.sqliteMediumPlugin local SQLite database access
Windowwindow.createLowCreate windows (auto-granted)
window.captureHighScreen capture

3. Risk Level Explanation

  • Low: Auto-granted or single confirmation
  • Medium: Requires explicit user authorization
  • High: Requires double confirmation with warning

4. Default Auto-granted Permissions

These permissions are automatically granted:

  • storage.plugin - Plugin private storage
  • clipboard.write - Write to clipboard
  • window.create - Create windows

SDK Version and Permission Enforcement

sdkapi Field

The sdkapi field determines whether permission checking is enabled:

sdkapi ValuePermission CheckNotes
Not declaredSkippedShows legacy SDK warning
< 251212SkippedShows legacy SDK warning
>= 251212EnabledFull permission enforcement

Migration Guide

If your plugin doesn't declare sdkapi or has a lower version:

EXAMPLE.JSON
{
  "sdkapi": 251212,
  "permissions": {
    "required": ["clipboard.read"],
    "optional": []
  }
}

Technical Notes

  • Permission enforcement is managed by the centralized permission center once sdkapi meets the threshold.
  • Grants are persisted at <appData>/config/permission/permissions.json and synchronized to the renderer via IPC.

API Reference

Check Permissions in Prelude (index.js)

EXAMPLE.JAVASCRIPT
const { permission } = globalThis

// Check if permission is granted
const hasPermission = await permission.check('clipboard.read')

// Request permission (triggers user confirmation dialog)
const granted = await permission.request('clipboard.read', 'Need to read clipboard content')

if (granted) {
  // Use clipboard API
  const text = clipboard.readText()
}

Use in Surface (Vue)

EXAMPLE.TYPESCRIPT
import { usePermission } from '@talex-touch/utils/plugin/sdk'

const { check, request, status } = usePermission()

// Check permission
const hasClipboard = await check('clipboard.read')

// Request permission
const granted = await request('network.internet', 'Need to access translation service')

// Get all permission status
const allStatus = await status()

SQLite SDK (sdkapi >= 260215)

When using usePluginSqlite(), declare storage.sqlite in required permissions:

EXAMPLE.JSON
{
  "sdkapi": 260215,
  "permissions": {
    "required": ["storage.sqlite"],
    "optional": []
  },
  "permissionReasons": {
    "storage.sqlite": "Store plugin business data in the local SQLite database"
  }
}
EXAMPLE.TS
import { usePluginSqlite } from '@talex-touch/utils/plugin/sdk'

const sqlite = usePluginSqlite()

await sqlite.execute(
  'CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, created_at TEXT NOT NULL)'
)

await sqlite.execute(
  'INSERT INTO notes (title, created_at) VALUES (?, ?)',
  ['hello', new Date().toISOString()]
)

const result = await sqlite.query<{ id: number; title: string }>(
  'SELECT id, title FROM notes ORDER BY id DESC LIMIT 10'
)
console.log(result.rows)

Best Practices

1. Principle of Least Privilege

Only declare permissions you actually need:

EXAMPLE.JSON
// ✅ Good
"permissions": {
  "required": ["clipboard.read"],
  "optional": []
}

// ❌ Avoid
"permissions": {
  "required": ["fs.read", "fs.write", "fs.execute", "system.shell"]
}

2. Provide Permission Reasons

EXAMPLE.JSON
"permissionReasons": {
  "clipboard.read": "Read text from clipboard for translation",
  "network.internet": "Connect to Google Translate API"
}

3. Graceful Degradation

EXAMPLE.JAVASCRIPT
async function translateText(text) {
  const hasNetwork = await permission.check('network.internet')
  
  if (!hasNetwork) {
    // Request permission or prompt user
    const granted = await permission.request('network.internet')
    if (!granted) {
      return { error: 'Network permission required for translation' }
    }
  }
  
  // Normal translation logic
  return await http.post('...')
}

4. Distinguish Required vs Optional

EXAMPLE.JSON
"permissions": {
  "required": ["clipboard.read"],  // Core functionality
  "optional": ["network.internet"] // Enhanced features
}

User Interface

Users can manage plugin permissions at:

  1. Plugin Details > Permissions Tab: View and manage single plugin permissions
  2. Runtime Dialog: Shown when plugin first requests permission

FAQ

Q: Why does my plugin show "Legacy SDK" warning?

A: Your manifest.json doesn't declare sdkapi or the version is below 251212. Add:

EXAMPLE.JSON
"sdkapi": 251212

Q: How to handle permission denial?

A: Provide degraded experience or clear error message:

EXAMPLE.JAVASCRIPT
const granted = await permission.request('clipboard.read')
if (!granted) {
  // Show hint, guide user to manual input or authorize
  feature.pushItems([{
    title: 'Clipboard Permission Required',
    subtitle: 'Please grant permission in plugin settings'
  }])
}

Q: Where is permission data stored?

A: Permission grants are stored in <appData>/config/permission/permissions.json.

Was this helpful?