Kunkun
Concepts

Permissions

Kunkun's capability-based permission model — declare, scope, request, and how it's enforced.

Extensions declare the capabilities they need in their manifest. Users approve them at install time; some sensitive operations additionally prompt at runtime. Enforcement always happens in the host — never in your plugin code.

Two forms of permission

Simple — a capability with no scope:

{ "kunkun": { "permissions": ["clipboard-read", "storage", "notifications"] } }

Scoped — an object with an allow/deny list or domain list:

{
  "permission": "fs-read",
  "allow": ["$EXTENSION_SUPPORT/**", "$HOME/Documents/**"],
  "deny": ["$HOME/Documents/Private/**"]
}

Permission catalogue

PermissionControlsScope shape
clipboard-read / clipboard-writeClipboard read / write
storageKV storage and record storage
notificationsSystem notifications
fs-read / fs-writeFile I/Oallow / deny glob arrays
networkfetchdomains glob array
shellCommand / script executioncommands: [{ program, args? }] or scripts
open-url / open-file / open-folderOpen with default appallow / deny
system-open / system-trash / system-finder / system-applications / system-selected-textSystem integrations
dialogNative file/save/message dialogs
window-controlResize/move/vibrancy/etc.
pathPath utilities & aliases
aiPlugin AI APIs
servicesDiscover/call other extensions' services
backendSpawn a managed backend processallow: [{ script, runtime?, capabilities? }]
input-monitorKeyboard/mouse event subscriptions

Path aliases

Scopes use cross-platform aliases resolved by the host:

AliasResolves to
$HOMEUser home
$DESKTOP / $DOWNLOAD / $DOCUMENTStandard user folders
$EXTENSIONYour install directory (read-only)
$EXTENSION_SUPPORTYour writable data directory

Globs: ** (recursive), * (one level), *.ext (extension match).

Least privilege

Prefer $EXTENSION_SUPPORT/** for your own data and request the narrowest scopes that work. Broad $HOME/** access is a red flag reviewers (and users) will notice.

Runtime (dynamic) grants

Some capabilities are best requested just before you need them, scoped to what the user is doing:

import { permissions } from "@kunkunsh/sdk";

const granted = await permissions.request(
  "fs-read",
  "$HOME/Documents/**",
  "Import the files you selected",   // shown in the prompt
);
if (granted) { /* … */ }
  • Allow Once → stored in a session map, cleared when the window closes.
  • Always Allow → persisted in the permission_grants table, user-revocable in settings.
  • Deny → returns false.

permissions.check(type, scope) is a preflight query — not an authorization boundary. Always let the host enforce.

How enforcement works

Notice scoped filesystem checks resolve symlinks before matching, so a symlink can't smuggle access outside your allow-list.

Service permission intersection

When extension A calls extension B's service, the effective permissions compose safely:

  • Caller resource permissions (fs, network, open-*) for user-chosen resources are preserved.
  • Provider implementation permissions (e.g. the provider's scoped shell access to ffmpeg) are supplied by the provider, so callers don't have to declare the provider's private details.
  • Everything else requires both sides.

This prevents privilege escalation: a low-permission extension can't gain capabilities by calling a high-permission service.

Raycast compatibility policy

Imported Raycast extensions run under a stricter policy. Each host method is tagged raycast-allowed or kunkun-only; Kunkun-only APIs (e.g. shell.spawn) are rejected for Raycast-sourced plugins even if the namespace is imported. Native Kunkun extensions get the full surface (still subject to manifest permissions).

Process sandbox

For Node modes and backends, the host derives runtime flags from your manifest:

RuntimeSandbox
Deno (preferred)--allow-read/--allow-write scoped to your paths, --allow-net=domain per declared domain, scoped --allow-run/--allow-ffi/--allow-env/--allow-sys
Node.js v25+Node permission flags; blanket env/sys are implicit (Node never gates them), but scoped env/sys/subprocess, domain-scoped network, and FFI are rejected unless capabilities.unsandboxed is declared
BunRejected unless capabilities.unsandboxed (Bun has no sandbox)

Your install directory is read-only; your support directory is writable. See Backend Processes for spawning managed child processes.

Checklist

  • Request the minimum permissions; scope filesystem/network tightly.
  • Use $EXTENSION_SUPPORT for your own data.
  • Request sensitive scopes at runtime with a clear reason.
  • Handle denial gracefully — check before acting, degrade rather than crash.
  • Document why you need each permission in your README.

On this page