Kunkun

Permissions

Understanding Kunkun's permission system

Kunkun uses a capability-based permission system to control what extensions can access. Extensions declare required permissions in their manifest, and users approve them at install time. Sensitive operations may require additional runtime prompts.

Permission Types

Simple Permissions

Boolean permissions that grant a specific capability:

{
  "permissions": [
    "clipboard-read",
    "clipboard-write",
    "notifications",
    "storage",
    "path",
    "shell",
    "dialog",
    "open-url",
    "open-file",
    "open-folder",
    "window-control"
  ]
}
PermissionDescription
clipboard-readRead clipboard content
clipboard-writeWrite to clipboard
notificationsShow system notifications
storageUse LocalStorage API
pathAccess path utilities
shellExecute shell commands
dialogShow native dialogs
open-urlOpen URLs in browser
open-fileOpen files with default app
open-folderOpen folders in file manager
window-controlControl window properties

Scoped Permissions

Permissions with specific scope/limitations:

Filesystem

{
  "permission": "fs-read",
  "allow": ["$HOME/Documents/**", "$DESKTOP/*.png"]
}
{
  "permission": "fs-write",
  "allow": ["$EXTENSION_SUPPORT/**"]
}

Network

{
  "permission": "network",
  "domains": ["*.github.com", "api.example.com"]
}

Shell

{
  "permission": "shell:execute",
  "allow": [
    {
      "command": "git",
      "args": ["clone", "pull", "push"]
    }
  ]
}

Path Aliases

Use path aliases for cross-platform compatibility:

AliasDescription
$HOMEUser home directory
$DESKTOPDesktop folder
$DOCUMENTDocuments folder
$DOWNLOADDownloads folder
$APPDATAApplication data folder
$EXTENSIONExtension root directory
$EXTENSION_SUPPORTExtension data storage

Glob Patterns

  • ** - Recursive (all subdirectories)
  • * - Single level
  • *.ext - File extension match
{
  "permission": "fs-read",
  "allow": [
    "$HOME/Documents/**",      // All files in Documents and subdirs
    "$DESKTOP/*.png",          // PNG files on desktop only
    "$EXTENSION_SUPPORT/data"  // Specific file/folder
  ]
}

Runtime Permission Prompts

Some operations trigger runtime prompts even if declared in the manifest:

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

// Request a permission at runtime
const granted = await permissions.request({
  permission: "fs-read",
  allow: ["$HOME/Downloads/**"]
});

if (granted) {
  // Permission was granted
  const files = await fs.readDir(downloadsPath);
}

Check Permission Status

// Check if a permission is currently granted
const hasAccess = await permissions.check({
  permission: "network",
  domains: ["api.github.com"]
});

if (!hasAccess) {
  // Request the permission
  await permissions.request({ /* ... */ });
}

List Current Grants

const grants = await permissions.listGrants();
// Returns all currently granted permissions for this extension

Grant Persistence

Permissions are persisted in the database:

  • Session grants: Valid until app restart
  • Permanent grants: Stored in permission_grants table

Grants include metadata:

  • grantedAt: Timestamp when granted
  • permissionType: Type of permission
  • scopePattern: The specific scope granted

Permission Enforcement

Plugin API Layer

All API calls go through permission checks:

// In the main process
async function readFile(path: string) {
  await requirePermission({ permission: "fs-read", path });
  // ... actual file read
}

Service Permission Intersection

When Plugin A calls Plugin B's service:

Effective Permissions = Caller Permissions ∩ Service Permissions
  • Simple permissions: Set intersection
  • Scoped permissions: Intersect allow lists, union deny lists

This prevents privilege escalation through service calls.

Development Mode

In development mode, you can bypass permission prompts:

{
  "kunkun": {
    "devMode": {
      "bypassPermissions": true
    }
  }
}

This is useful for testing but should never be used in production extensions.

Permission Schema

Full permission type definition:

type Permission =
  | string // Simple permission like "clipboard-read"
  | {
      permission: "fs-read" | "fs-write";
      allow?: string[];
      deny?: string[];
    }
  | {
      permission: "network";
      domains: string[];
    }
  | {
      permission: "shell:execute" | "shell:spawn";
      allow: Array<{
        command: string;
        args?: string[];
      }>;
    };

Best Practices

  1. Request minimum permissions: Only ask for what you need
  2. Use scoped permissions: Limit filesystem/network access to specific paths/domains
  3. Handle denials gracefully: Check permissions before operations
  4. Document requirements: Explain why each permission is needed
  5. Request at runtime for sensitive ops: Ask just before performing the action

Example: Full Permission Declaration

{
  "kunkun": {
    "identifier": "com.example.my-extension",
    "permissions": [
      "clipboard-read",
      "clipboard-write",
      "notifications",
      "storage",
      "path",
      {
        "permission": "fs-read",
        "allow": [
          "$HOME/Documents/**",
          "$DESKTOP/**"
        ]
      },
      {
        "permission": "fs-write",
        "allow": [
          "$EXTENSION_SUPPORT/**"
        ]
      },
      {
        "permission": "network",
        "domains": [
          "*.github.com",
          "api.example.com"
        ]
      }
    ]
  }
}

This grants:

  • Clipboard read/write access
  • Notification permission
  • Local storage access
  • Path utilities
  • Read access to Documents and Desktop
  • Write access to extension support directory only
  • Network access to GitHub and example.com APIs

On this page