Permissions

Ghosty plugins run in a sandboxed environment with no default access to platform features. To use storage, make network requests, show notifications, or navigate the host application, a plugin must declare the required permissions in its plugin.yaml manifest.

How permissions work

  1. Declaration -- the developer lists required permissions in plugin.yaml.
  2. Install prompt -- when a user installs the plugin, Ghosty displays a consent dialog listing every requested permission with a plain-language explanation.
  3. Runtime enforcement -- the GhostySDK checks permissions on every API call. If a plugin tries to call an API it has not been granted access to, the call throws a PermissionDeniedError.
  4. Revocation -- users can revoke individual permissions at any time from the plugin settings screen. Your plugin should handle PermissionDeniedError gracefully and degrade functionality rather than crashing.
Principle of least privilege

Only request the permissions your plugin genuinely needs. Every additional permission increases the user's trust barrier and the scrutiny applied during review.

Storage permissions

Storage permissions control access to the plugin's private key-value store. Each plugin has an isolated storage namespace; plugins cannot read or write another plugin's data.

Storage permissions

PermissionDescriptionRisk
storage.readRead values from the plugin's private key-value store.low
storage.writeWrite, update, and delete values in the plugin's private key-value store.low
storage.watchSubscribe to real-time change events on storage keys.low

Storage limits

MetricLimit
Max keys per plugin1,000
Max value size64 KB
Total storage per plugin10 MB
Watch subscriptions50 concurrent

Notification permissions

Notification permissions control a plugin's ability to display system notifications and in-app toasts.

Notification permissions

PermissionDescriptionRisk
notifications.showDisplay in-app toast notifications to the user.medium
notifications.systemSend OS-level system notifications (may appear even when Ghosty is in the background).high
notifications.badgeUpdate the plugin's badge count shown on its icon in the dashboard.low
System notification abuse

Plugins that send excessive or spammy system notifications will be flagged during review and may be delisted from the plugin store. Use system notifications only for time-sensitive, user-initiated alerts.

Network permissions

Network permissions control outbound HTTP requests and WebSocket connections.

Network permissions

PermissionDescriptionRisk
network.fetchMake outbound HTTP/HTTPS requests to external APIs. The target domain must be declared in the manifest's allowed_domains list.high
network.websocketOpen persistent WebSocket connections to declared domains.high

Domain allowlist

When requesting network.fetch or network.websocket, you must also declare the specific domains your plugin will communicate with:

permissions:
  - network.fetch
 
allowed_domains:
  - api.openweathermap.org
  - api.example.com

The runtime blocks any fetch or WebSocket connection to a domain not in this list. Wildcard patterns are not supported.

No wildcards

You cannot use * or glob patterns in allowed_domains. Every domain must be explicitly listed. This is a deliberate security measure to prevent data exfiltration.

Navigation permissions

Navigation permissions control the plugin's ability to interact with the Ghosty host application's navigation stack.

Navigation permissions

PermissionDescriptionRisk
navigation.open_urlOpen URLs in the user's default browser.medium
navigation.deep_linkNavigate the Ghosty host to another plugin or built-in screen using deep links.medium
navigation.fullscreenTransition the plugin from widget mode to fullscreen mode.low

User context permissions

User context permissions control access to information about the currently logged-in Ghosty user.

User context permissions

PermissionDescriptionRisk
user.profileRead the user's display name and avatar URL (no email or private data).low
user.preferencesRead the user's locale, timezone, and theme preference.low
user.idRead the user's unique anonymous identifier. Cannot be correlated to personal data.medium

Handling permission denial

When a user revokes a permission or the plugin attempts an unpermitted action, the SDK throws a PermissionDeniedError. Always wrap SDK calls in try/catch blocks and provide meaningful fallback behaviour:

import { ghosty, PermissionDeniedError } from "@ghosty/sdk";
 
async function loadSavedCity(): Promise<string> {
  try {
    const city = await ghosty.storage.get("selected_city");
    return city ?? "London";
  } catch (error) {
    if (error instanceof PermissionDeniedError) {
      // Storage permission was revoked -- fall back to default
      console.warn("Storage permission denied, using default city.");
      return "London";
    }
    throw error; // Re-throw unexpected errors
  }
}

Permission changes between versions

When you release a new version that requires additional permissions, Ghosty prompts the user to approve the new permissions before updating. If the user declines, they stay on the previous version.

To minimise friction:

  • Add new permissions only when genuinely needed.
  • Use the description field in your store listing update notes to explain why new permissions are required.
  • Design your plugin so that new features behind new permissions are optional, not blocking.
Gradual adoption

Consider releasing permission-heavy features as opt-in. Request the new permission only when the user attempts to use the feature, rather than upfront. This pattern is supported via ghosty.permissions.request().