Permissions
Understand how the permission system works and how to declare permissions in your extension.
Overview
Extensions must declare all required permissions in their manifest.json. Users review and approve these permissions during installation. This ensures transparency and gives users control over what extensions can access.
Granted
Permission is approved
Denied
Permission was rejected
Ask
Will prompt the user
{
"permissions": {
"database": [
{ "target": "MCowBQYDK2Vw...__password-manager__credentials", "operation": "read" },
{ "target": "MCowBQYDK2Vw...__password-manager__categories", "operation": "read_write" }
],
"filesystem": [
{ "target": "exports/**", "operation": "read_write" }
],
"http": [
{ "target": "https://api.example.com/**" }
],
"shell": null
}
}
Permission Types
Database
Read and write data to SQLite tables
Filesystem
Save and open files through native dialogs
HTTP
Make web requests to external APIs
Shell
Execute system commands (restricted)
Database Permissions
Database permissions control access to tables from other extensions. The target must be the full table name in the format ____:
Extensions can create, read, and modify their own tables within their namespace without any permissions. Permissions are only required when accessing tables from other extensions.
// Access another extension's table
// Format: {publicKey}__{extensionName}__{tableName}
{ "target": "MCowBQYDK2Vw...__password-manager__credentials", "operation": "read" }
// Full access to another extension's table
{ "target": "MCowBQYDK2Vw...__password-manager__categories", "operation": "read_write" }
// Access all tables of a specific extension (use sparingly)
{ "target": "MCowBQYDK2Vw...__password-manager__*", "operation": "read" }
Accessing Dependency Tables
import { useHaexVault } from '@haex-space/vault-sdk/vue'
const { client } = useHaexVault({ manifest })
// Access another extension's table using getDependencyTableName
const depTable = client.getDependencyTableName(
'MCowBQYDK2VwAyEA7x8Z9Kq3mN2pL5tR8vW4yB6cE1fH3gJ9kM7nP0qS2uV',
'password-manager',
'credentials'
)
// Query the dependency table (requires permission in manifest)
const credentials = await client.query(`SELECT * FROM ${depTable}`)
Filesystem Permissions
Filesystem permissions use glob patterns to specify which files your extension can access:
// Read text files anywhere
{ "target": "**/*.txt", "operation": "read" }
// Read and write in a specific directory
{ "target": "exports/**", "operation": "read_write" }
// Full access to JSON files in documents
{ "target": "documents/*.json", "operation": "read_write" }
All filesystem operations show native dialogs to the user, providing an additional layer of security.
HTTP Permissions
HTTP permissions specify which URLs your extension can make requests to:
// All endpoints on a specific API
{ "target": "https://api.example.com/**" }
// Specific path pattern
{ "target": "https://api.github.com/users/*/repos" }
// Multiple subdomains
{ "target": "https://*.example.com/api/*" }
Pattern Syntax
**- Matches any path segments*- Matches a single path segment
Checking Permissions
Before performing an operation, you can check if the permission is granted:
import { useHaexClient } from '@haex-space/vault-sdk/vue'
const client = useHaexClient()
// Check database permission
const canReadUsers = await client.checkDatabaseAsync('users', 'read')
const canWriteUsers = await client.checkDatabaseAsync('users', 'write')
// Check filesystem permission
const canReadExports = await client.checkFilesystemAsync('exports/', 'read')
// Check HTTP permission
const canFetchApi = await client.checkWebAsync('https://api.example.com/data')
// Use in conditional logic
if (canWriteUsers) {
await client.insert('users', { name: 'John', email: 'john@example.com' })
} else {
console.warn('No write permission for users table')
}
Requesting Permissions
You can request permissions at runtime, which will prompt the user:
import { useHaexClient } from '@haex-space/vault-sdk/vue'
const client = useHaexClient()
// Request permission with reason
const response = await client.requestDatabasePermission(
'users',
'write',
'Required to save your profile changes'
)
if (response.status === 'granted') {
// Permission granted, proceed with operation
await client.insert('users', userData)
} else if (response.status === 'denied') {
// User denied permission
showError('Permission denied. Cannot save data.')
} else if (response.status === 'ask') {
// System will show permission dialog
// Handle async response
}
Permission Status Values
// Permission status values
enum PermissionStatus {
GRANTED = 'granted', // Permission is granted
DENIED = 'denied', // Permission was denied
ASK = 'ask' // Will prompt user
}
interface PermissionResponse {
status: PermissionStatus
permanent: boolean // If true, won't ask again
}
Best Practices
Request Minimal Permissions
Only request permissions your extension actually needs. Users are more likely to install extensions with fewer permission requests.
Be Specific
Use specific table names and URL patterns instead of wildcards. This makes your extension's behavior more predictable and trustworthy.
Explain Why
When requesting permissions at runtime, provide a clear reason why the permission is needed.
Handle Denials Gracefully
If a permission is denied, provide a good user experience. Show a helpful message and offer alternatives if possible.
Extensions that request excessive permissions may be flagged during marketplace review.