Permissions

10. Permissions Reference

Declare every permission your extension needs in manifest.json:

{
  "permissions": ["network", "notifications:send", "clipboard:read"]
}

Full permissions table

Permission What it unlocks SDK methods that require it
network Outbound HTTP requests NetworkService.fetch()
notifications:send System notification center NotificationService.notify(), .checkPermission(), .requestPermission(), .registerActionTypes(), .listenForActions(), .createChannel()
clipboard:read Read clipboard content and history ClipboardHistoryService.readCurrentClipboard(), .getRecentItems()
clipboard:write Write and manipulate clipboard ClipboardHistoryService.writeToClipboard(), .pasteItem(), .simulatePaste(), .toggleItemFavorite(), .deleteItem(), .clearNonFavorites()
storage:read Read from extension key-value store StorageService.get(), .getAll()
storage:write Write to extension key-value store StorageService.set(), .delete(), .clear()
fs:read Read files from the filesystem Future FileService.read(), .list()
fs:write Write files to the filesystem Future FileService.write(), .delete()
fs:watch Observe filesystem changes under declared glob patterns. Scope is provided by permissionArgs["fs:watch"] — a string[] of globs (must resolve under $HOME or /tmp). Without the sidecar patterns, declaring fs:watch alone is rejected at manifest load. See FileSystemWatcherService. FileSystemWatcherService.watch()
shell:spawn Spawn arbitrary OS processes and stream their stdout/stderr output ShellService.spawn()
shell:open-url Open a URL in the system browser window.parent.postMessage({ type: 'asyar:api:opener:open', url })
entitlements:read Read the user's active subscription entitlements EntitlementService.check(), .getAll()
selection:read Read the user's currently selected text or selected file-manager items from the frontmost application SelectionService.getSelectedText(), .getSelectedFinderItems()
ai:use Stream responses from the user's configured AI provider AIService.stream()
oauth:use Run an OAuth 2.0 PKCE authorization flow with a third-party provider OAuthService.authorize(), .revokeToken()
extension:invoke Invoke a command in another installed extension InteropService.launchCommand()
application:read One-shot queries on the application:* namespace (frontmost app, installed-app index, isRunning) and the applicationIndex:* push subscription (onApplicationsChanged — fires when an app is installed / removed or the user edits settings.search.additionalScanPaths). Index events carry the same data class as listApplications, so the same permission gates both surfaces. ApplicationService.getFrontmostApplication(), .listApplications(), .syncApplicationIndex(), .isRunning(), .onApplicationsChanged()
window:manage Read and set the position, size, and fullscreen state of the frontmost OS window. macOS requires Accessibility permission; Linux requires xdotool; Wayland not supported. WindowManagementService.getWindowBounds(), .setWindowBounds(), .setFullscreen()
power:inhibit Prevent the OS from sleeping while extension logic is running. macOS uses IOKit power assertions; Linux uses logind DBus (non-systemd systems return PowerUnavailable); Windows uses SetThreadExecutionState. PowerService.keepAwake(), .release(), .list()
systemEvents:read Subscribe to OS-level push events: sleep, wake, lid open/close, battery level, and AC/battery power-source changes. macOS uses IORegisterForSystemPower + IOKit polling; Linux and Windows watchers are stubs (subscriptions succeed but events never fire yet). SystemEventsService.onSystemSleep(), .onSystemWake(), .onLidOpen(), .onLidClose(), .onBatteryLevelChange(), .onPowerSourceChange()
app:frontmost-watch Subscribe on the appEvents:* namespace to application-presence push events: launched, terminated, frontmost-changed. macOS uses NSWorkspace.notificationCenter; Windows uses WMI + SetWinEventHook(EVENT_SYSTEM_FOREGROUND); Linux uses /proc polling + DBus NameOwnerChanged + (X11 only) _NET_ACTIVE_WINDOW. Wayland sessions get launch/terminate but no frontmost events. Note the namespace split: application:* is query-only and stays under application:read; only the push subscriptions require this permission. ApplicationService.onApplicationLaunched(), .onApplicationTerminated(), .onFrontmostApplicationChanged()
runs:track Track long-running work via RunService. Lets your extension start, write to, complete, fail, and cancel runs that surface in the launcher's runs UI and tray badge. RunService.start(), RunHandle.write(), .done(), .fail(), .cancel()
tools:register Register tools your extension exports to the agent runtime. Tools appear in the agent tool-picker and can be invoked during a tool-calling round. ToolsService.registerTool(), .unregisterTool(), .listTools()
snippets:contribute Contribute a { :shortcode: → expansion } dictionary to the launcher's global keystroke matcher. Typed shortcodes are replaced in-place in any text input on any app. User-created snippets shadow extension contributions on key collision. Keys must match ^:[a-z0-9_+-]{1,32}:$. See SnippetsService. SnippetsService.registerShortcodes(), .unregisterShortcodes()

What happens if a permission is missing

When your extension calls a method that requires an undeclared permission, the host's permission gate intercepts the postMessage before it reaches any service implementation. The gate returns a structured error immediately:

// Attempting to call send() without "notifications:send" in manifest:
try {
  await notif.send({ title: 'Hi', body: 'World' });
} catch (err) {
  // err.message: 'Extension "com.yourname.ext" called "asyar:api:notifications:send"
  //               but did not declare permission "notifications:send" in its manifest.json'
}

The extension is not suspended or crashed — it continues running. Only that specific blocked call fails.

Principle of least privilege

Only declare permissions you actually use. Reviewers inspect the permissions list during store review and will reject extensions with undeclared or unnecessary permissions.

Capture-time clipboard exclusions (host policy)

Asyar's host enforces a privacy filter at clipboard capture time. Items that match any of the following are never stored locally and therefore never sync, never appear in extension reads, and never reach the diagnostics channel:

  • macOS: pasteboards carrying org.nspasteboard.ConcealedType, org.nspasteboard.TransientType, or org.nspasteboard.AutoGeneratedType, or Apple's com.apple.pasteboard.promised-type-NSStringPboardTypeAutoGenerated.
  • Windows: pasteboards registered with CanIncludeInClipboardHistory or ExcludeClipboardContentFromMonitorProcessing.
  • All platforms: a configurable source-app denylist. Defaults cover 1Password, Bitwarden, KeePassXC, Dashlane, Enpass, LastPass, and Apple Keychain Access.

This is host policy — extensions cannot disable it, bypass it, or extend it from the manifest. Users edit the source-app denylist in Settings → Privacy → Clipboard Privacy.

Secret-format redaction (host policy)

In addition to capture-time exclusions, Asyar runs a regex-based secret detector on every clipboard text/HTML/RTF item, snippet expansion, and AI conversation message. Detected secrets (AWS keys, GitHub tokens, JWTs, PEM private keys, Luhn-valid credit cards, and others — see the full catalog in Settings → Privacy → Secret Redaction) are replaced in place with [redacted: <kind>] before storage. Extensions reading clipboard history through IClipboardHistoryService only ever see the redacted form. AI providers receive the redacted form too. The user can disable the detector globally or per-category.

See docs/explanation/clipboard-privacy.md for the full layered defense design.