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, and plugins that do not declare a valid sdkapi are blocked by runtime.
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
{
"id": "com.example.plugin",
"name": "My Plugin",
"version": "1.0.0",
"sdkapi": 260428,
"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
| Category | Permission ID | Risk Level | Description |
|---|---|---|---|
| Filesystem | fs.read | Medium | Read user files |
fs.write | High | Write/modify files | |
fs.execute | High | Execute files or scripts | |
| Clipboard | clipboard.read | Medium | Read clipboard content |
clipboard.write | Low | Write to clipboard | |
| Network | network.local | Low | Access local network |
network.internet | Medium | Access internet | |
network.download | Medium | Download files | |
| System | system.shell | High | Execute system commands |
system.notification | Low | Send system notifications | |
system.tray | Medium | Operate system tray | |
| Intelligence | intelligence.basic | Low | Basic intelligence capabilities |
intelligence.admin | High | Admin intelligence capabilities | |
intelligence.agents | High | Agent system | |
| Storage | storage.plugin | Low | Plugin private storage (auto-granted) |
storage.shared | Medium | Cross-plugin shared storage | |
storage.sqlite | Medium | Plugin local SQLite database access | |
| Window | window.create | Low | Create windows (auto-granted) |
window.capture | High | Screen 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 storageclipboard.write- Write to clipboardwindow.create- Create windows
SDK Version and Permission Enforcement
sdkapi Field
The sdkapi field determines whether permission checking can enter the runtime:
| sdkapi Value | Permission Check | Notes |
|---|---|---|
| Not declared | Blocked | Runtime returns SDKAPI_BLOCKED |
| < 251212 | Blocked | Runtime returns SDKAPI_BLOCKED |
| Not in the supported list | Blocked | Non-canonical or future markers return SDKAPI_BLOCKED |
| Supported and >= 251212 | Enabled | Full permission enforcement |
Migration Guide
If your plugin doesn't declare sdkapi or has a lower version:
{
"sdkapi": 260428,
"permissions": {
"required": ["clipboard.read"],
"optional": []
}
}
Technical Notes
- Permission enforcement is managed by the centralized permission center once
sdkapimeets the threshold. - Grants are persisted in the SQLite-backed permission store under
<appData>/config/permission/; legacypermissions.jsonis migration-only and no longer the writable source of truth.
API Reference
Check Permissions in Prelude (index.js)
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)
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:
{
"sdkapi": 260428,
"permissions": {
"required": ["storage.sqlite"],
"optional": []
},
"permissionReasons": {
"storage.sqlite": "Store plugin business data in the local SQLite database"
}
}
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:
// ✅ Good
"permissions": {
"required": ["clipboard.read"],
"optional": []
}
// ❌ Avoid
"permissions": {
"required": ["fs.read", "fs.write", "fs.execute", "system.shell"]
}
2. Provide Permission Reasons
"permissionReasons": {
"clipboard.read": "Read text from clipboard for translation",
"network.internet": "Connect to Google Translate API"
}
3. Graceful Degradation
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
"permissions": {
"required": ["clipboard.read"], // Core functionality
"optional": ["network.internet"] // Enhanced features
}
User Interface
Users can manage plugin permissions at:
- Plugin Details > Permissions Tab: View and manage single plugin permissions
- Runtime Dialog: Shown when plugin first requests permission
FAQ
Q: Why does my plugin show SDKAPI_BLOCKED?
A: Your manifest.json doesn't declare sdkapi, uses an invalid / unsupported marker, or the version is below 251212. Update it to the current supported marker:
"sdkapi": 260428
Q: How to handle permission denial?
A: Provide degraded experience or clear error message:
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 the SQLite-backed permission store under <appData>/config/permission/; permissions.json only exists as a legacy migration source when upgrading old installs.