Skip to content

Permissions Reference ​

BluePLM extensions use a permission system to control access to APIs. Users see requested permissions before installing an extension. This document covers all available permissions in detail.

Overview ​

Permissions are declared in your manifest:

json
{
  "permissions": {
    "client": ["ui:toast", "storage:local", "network:orgApi"],
    "server": ["storage:database", "secrets:read", "http:domain:api.example.com"]
  }
}

Security Model ​

PrincipleDescription
Least PrivilegeOnly request permissions you actually need
Explicit DeclarationAll permissions must be declared in manifest
Runtime EnforcementAPI calls without permission throw errors
User VisibilityUsers see permissions before installation

Client Permissions ​

Client permissions control what the extension can do in the Extension Host (client-side).

UI Permissions ​

ui:toast ​

Display toast notifications.

typescript
api.ui.showToast('Message', 'success')

Use for: Brief, non-blocking feedback messages.


ui:dialog ​

Show modal dialogs and input prompts.

typescript
await api.ui.showDialog({ title: 'Confirm', message: '...', type: 'confirm' })
await api.ui.showQuickPick([...])
await api.ui.showInputBox({ ... })

Use for: User confirmations, selections, and text input.


ui:status ​

Set the extension's connection status indicator.

typescript
api.ui.setStatus('online')  // or 'offline', 'partial', 'checking'

Use for: Showing connection state to external services.


ui:progress ​

Show progress indicators during operations.

typescript
await api.ui.showProgress(
  { title: 'Working...', cancellable: true },
  async (progress, token) => {
    progress.report({ message: 'Step 1...', increment: 25 })
    // ...
  }
)

Use for: Long-running operations with visual feedback.


ui:quickpick ​

Show quick pick selection lists.

typescript
const selected = await api.ui.showQuickPick([
  { label: 'Option 1' },
  { label: 'Option 2' }
])

Note: This is typically grouped with ui:dialog.


ui:inputbox ​

Show text input dialogs.

typescript
const value = await api.ui.showInputBox({
  title: 'Enter value',
  validateInput: (v) => v ? undefined : 'Required'
})

Note: This is typically grouped with ui:dialog.


Storage Permissions ​

storage:local ​

Access extension-scoped local storage.

typescript
await api.storage.set('key', value)
const data = await api.storage.get('key')
await api.storage.delete('key')
const keys = await api.storage.keys()

Use for: Persisting user preferences, cache data, and extension state.

Data isolation: Each extension has its own storage namespace. Extensions cannot access each other's data.


Network Permissions ​

network:orgApi ​

Call the organization's API server (authenticated).

typescript
const response = await api.callOrgApi('/extensions/myext/sync', {
  method: 'POST',
  body: { vaultId: '...' }
})

Use for: Calling your extension's server-side handlers.

Automatically includes: User authentication headers.


network:storeApi ​

Call the Extension Store API.

typescript
const response = await api.callStoreApi('/store/extensions/myext')

Use for: Checking for updates, extension metadata.


network:fetch ​

Make HTTP requests to external URLs (client-side).

typescript
const response = await api.fetch('https://api.example.com/data', {
  method: 'GET'
})

Important: Requires corresponding http:domain:* server permissions.

Use for: Client-side external API calls (rare; prefer server-side).


Command Permissions ​

commands:register ​

Register command handlers.

typescript
context.subscriptions.push(
  api.commands.registerCommand('myext.doThing', handler)
)

Use for: Creating executable commands.


commands:execute ​

Execute registered commands.

typescript
await api.commands.executeCommand('myext.doThing', arg1, arg2)

Use for: Triggering commands programmatically.


Workspace Permissions ​

workspace:files ​

Access file change events and open file information.

typescript
context.subscriptions.push(
  api.workspace.onFileChanged((events) => {
    // Handle file changes
  })
)

const openFiles = await api.workspace.getOpenFiles()

Use for: Reacting to file changes, monitoring workspace.


workspace:vaults ​

Access vault configuration.

typescript
const vault = await api.workspace.getCurrentVault()
const allVaults = await api.workspace.getVaults()

Use for: Multi-vault operations, vault-aware sync.


Telemetry Permission ​

telemetry ​

Send anonymous analytics data.

typescript
api.telemetry.trackEvent('sync_completed', { fileCount: 42 })
api.telemetry.trackError(error, { context: 'sync' })
api.telemetry.trackTiming('operation', 1500)

Use for: Gathering anonymous usage statistics.

Privacy: All telemetry is anonymized and aggregated.


Server Permissions ​

Server permissions control what extension handlers can do in the V8 sandbox.

Storage Permissions ​

storage:database ​

Access extension-scoped database storage.

typescript
await api.storage.set('key', value)
const data = await api.storage.get('key')
await api.storage.delete('key')
const keys = await api.storage.list('prefix:')

Use for: Persisting extension data server-side.

Data isolation: Scoped to (organization, extension) combination.


Secrets Permissions ​

secrets:read ​

Read encrypted secrets.

typescript
const apiKey = await api.secrets.get('api_key')

Use for: Accessing stored credentials.

Audit: All read operations are logged.


secrets:write ​

Store and delete encrypted secrets.

typescript
await api.secrets.set('api_key', 'sk-...')
await api.secrets.delete('api_key')

Use for: Saving OAuth tokens, API keys.

Limits: 50 secrets max, 10KB each.

Audit: All write operations are logged.


HTTP Permissions ​

http:fetch ​

Make HTTP requests to any domain (unrestricted).

json
"server": ["http:fetch"]

⚠️ Warning: This grants access to ALL domains. Prefer specific domain permissions.


http:domain: ​

Make HTTP requests to a specific domain.

json
"server": [
  "http:domain:api.googleapis.com",
  "http:domain:accounts.google.com",
  "http:domain:api.example.com"
]

Pattern support:

  • Exact: http:domain:api.example.com
  • Wildcards: Not supported (declare each domain)

Example:

typescript
// With http:domain:api.googleapis.com permission
const response = await api.http.fetch('https://api.googleapis.com/v3/files')
// ✓ Allowed

const response = await api.http.fetch('https://malicious.com/steal')
// ✗ Error: Domain not in allowed list

Permission Groups ​

Common permission combinations for different extension types.

Basic UI Extension ​

json
{
  "permissions": {
    "client": [
      "ui:toast",
      "ui:dialog",
      "storage:local",
      "commands:register"
    ]
  }
}

Cloud Sync Extension ​

json
{
  "permissions": {
    "client": [
      "ui:toast",
      "ui:dialog",
      "ui:status",
      "ui:progress",
      "storage:local",
      "network:orgApi",
      "commands:register",
      "workspace:files"
    ],
    "server": [
      "storage:database",
      "secrets:read",
      "secrets:write",
      "http:domain:api.cloudprovider.com"
    ]
  }
}

OAuth Integration ​

json
{
  "permissions": {
    "client": [
      "ui:toast",
      "ui:dialog",
      "network:orgApi"
    ],
    "server": [
      "secrets:read",
      "secrets:write",
      "http:domain:oauth2.googleapis.com",
      "http:domain:accounts.google.com",
      "http:domain:api.googleapis.com"
    ]
  }
}

File Watcher Extension ​

json
{
  "permissions": {
    "client": [
      "ui:toast",
      "storage:local",
      "workspace:files",
      "telemetry"
    ]
  }
}
json
{
  "permissions": {
    "client": [
      "ui:toast",
      "ui:dialog",
      "ui:status",
      "ui:progress",
      "storage:local",
      "network:orgApi",
      "commands:register",
      "commands:execute",
      "workspace:files",
      "workspace:vaults",
      "telemetry"
    ],
    "server": [
      "storage:database",
      "secrets:read",
      "secrets:write",
      "http:domain:api.service.com",
      "http:domain:auth.service.com"
    ]
  }
}

Runtime Enforcement ​

Permission Denied Errors ​

Calling an API without permission throws an error:

typescript
// Without ui:toast permission:
api.ui.showToast('Hello')
// Error: Permission denied: ui:toast not granted to extension myext

// Without http:domain:api.example.com permission:
await api.http.fetch('https://api.example.com/data')
// Error: Domain api.example.com not in extension's allowed domains

Checking Permissions ​

Extensions should handle permission errors gracefully:

typescript
try {
  api.ui.showToast('Success!', 'success')
} catch (error) {
  if (error.message.includes('Permission denied')) {
    console.log('Toast permission not available, using fallback')
    // Alternative feedback method
  } else {
    throw error
  }
}

Best Practices ​

1. Request Minimal Permissions ​

json
// ❌ Bad: Requesting everything
"permissions": {
  "client": ["ui:toast", "ui:dialog", "ui:status", "ui:progress", ...],
  "server": ["http:fetch"]  // Unrestricted!
}

// ✓ Good: Only what's needed
"permissions": {
  "client": ["ui:toast", "storage:local"],
  "server": ["http:domain:api.myservice.com"]
}

2. Document Why Permissions Are Needed ​

In your README:

markdown
## Permissions

This extension requires:
- **ui:toast**: Show sync status notifications
- **storage:local**: Remember sync preferences
- **http:domain:api.myservice.com**: Connect to your account

3. Handle Missing Permissions Gracefully ​

typescript
// Check if feature is available before using
async function showProgress(task) {
  try {
    return await api.ui.showProgress({ title: 'Working...' }, task)
  } catch (e) {
    // Fallback without progress UI
    return await task({ report: () => {} }, { isCancellationRequested: false })
  }
}

4. Use Specific HTTP Domains ​

json
// ❌ Avoid: Too broad
"server": ["http:fetch"]

// ✓ Better: Specific domains
"server": [
  "http:domain:api.googleapis.com",
  "http:domain:oauth2.googleapis.com"
]

5. Separate Read and Write Secrets ​

Only request secrets:write if you actually store secrets:

json
// Read-only (using pre-configured secrets)
"server": ["secrets:read"]

// Read and write (OAuth flow stores tokens)
"server": ["secrets:read", "secrets:write"]

Permission Changes ​

Adding Permissions ​

Adding new permissions requires users to re-approve the extension:

  1. User sees "Extension wants additional permissions"
  2. New permissions are listed
  3. User approves or rejects

Removing Permissions ​

Removing permissions is transparent to users — no approval needed.


User-Facing Permission Descriptions ​

PermissionUser-Friendly Description
ui:toastShow notifications
ui:dialogDisplay dialogs and prompts
ui:statusShow connection status
ui:progressDisplay progress indicators
storage:localStore preferences locally
network:orgApiCommunicate with your organization
commands:registerAdd keyboard shortcuts
workspace:filesMonitor file changes
workspace:vaultsAccess vault information
telemetrySend anonymous usage data
storage:databaseStore data on server
secrets:readAccess stored credentials
secrets:writeSave credentials securely
http:domain:*Connect to

← Contributions Reference | Package Format →