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 |
Background delivery through the system notification center | FeedbackService.sendBackground(), .dismissBackground() |
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 |
Reveal a file in the system file manager (Finder/Explorer). Does not grant file content read — see files:read. |
FileManagerService.showInFileManager() |
fs:write |
Move a file to the system trash. | FileManagerService.trash() |
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 URLs. Bare shell:open-url allows the web-default schemes (http, https, mailto, tel); permissionArgs["shell:open-url"] — a string[] of lowercase scheme names, exact-matched, no globs — extends that set so protocol handlers (steam://, vscode://, …) can be launched without shell:spawn. Schemeless/relative URLs are rejected. The same declared-scheme allowlist gates BrowserService.openUrl()'s OS-default fallback. |
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() |
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() |
screen:pick-color |
Show the OS eyedropper and read the sRGB color of one screen pixel the user picks. macOS uses NSColorSampler (native loupe, no Screen Recording permission); Linux uses the XDG portal Screenshot.PickColor (native loupe) with an X11 crosshair-grab fallback; Windows uses a click-to-pick crosshair (GetPixel). Resolves null when the user cancels with Esc. |
ScreenService.pickColor() |
process:read |
List running processes grouped by app, with CPU/memory usage. | ProcessService.list() |
process:kill |
Terminate or force-kill processes; OS-critical processes require explicit confirmation. | ProcessService.kill() |
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() |
files:search |
Search the same bounded, cached local file index that backs the host's "Search Files" view. Does not grant file content read — see files:read for that. |
FilesService.search(), .status() |
files:read |
Bounded text read (lossy UTF-8, 1 MiB ceiling) of files matching declared glob patterns. Scope is provided by permissionArgs["files:read"] — a string[] of globs, and those globs are the entire readable scope (the home/app-data/temp roots host code can read are not included). Unlike fs:watch, patterns may anchor anywhere — other drives included — or nowhere (**/steamapps/appmanifest_*.acf). Credential stores (~/.ssh, ~/.aws, …) and OS locations are denied even when a declared pattern covers them. Also grants scoped filename enumeration (glob, for content-addressed names unknowable in advance) and thumbnails (thumbnail, an asyar-thumb:// URL from the launcher's own preview pipeline; images only, identical on every platform) over the same declared globs — each strictly less information than the byte read. |
FilesService.read(), .glob(), .thumbnail() |
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:feedback:sendBackground"
// 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, ororg.nspasteboard.AutoGeneratedType, or Apple'scom.apple.pasteboard.promised-type-NSStringPboardTypeAutoGenerated. - Windows: pasteboards registered with
CanIncludeInClipboardHistoryorExcludeClipboardContentFromMonitorProcessing. - 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.