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

read
read_write

Filesystem

Save and open files through native dialogs

read
read_write

HTTP

Make web requests to external APIs

(target only)

Shell

Execute system commands (restricted)

execute

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.