Architecture
The multi-shell/core design that keeps Kunkun portable — and what it means for your extension.
Kunkun's defining decision is that it is not an Electron app. Electron is the current desktop shell — not the product. The same backend/core is designed to run from Electron today, and from a CLI/TUI, a headless server, a browser UI, or a native shell tomorrow, by swapping adapters at the edges.
For you as an extension author, this has one practical consequence: your extension talks to a typed HostAPI, not to Electron. The host it runs under is an implementation detail. A worker-view extension you write for the desktop app can run unchanged when the same core is driven by a CLI or a browser.
One core, many shells
The layered model
Requests flow through the same layers no matter which shell you run:
Only the top (interfaces) and bottom (adapters) layers change per shell. The middle — API views, business logic, permission enforcement, the plugin runtime, the database — is shared and shell-agnostic.
Ports and adapters
Kunkun follows a strict ports-and-adapters split:
- The backend/core owns business logic, permission enforcement, plugin orchestration, the database, and process-lifecycle policy.
- Shell adapters own platform concerns: windows, menus, protocol registration, OS keychain, native clipboard, process spawning, native filesystem/watchers.
- Transport is a choice, not part of business logic. The same core method is reachable over Electron IPC, a WebSocket, stdio, a Worker
postMessage, or an in-process call.
Contributor rule of thumb
Portable-looking logic (schema, migrations, queries, validation, service contracts, plugin orchestration, permission composition) belongs in a package, not in apps/desktop/electron. Electron files should only contain things that genuinely need BrowserWindow, the tray, protocol registration, the OS keychain, native dialogs, or process spawning.
Where code lives
| Package | Responsibility | Imports Electron? |
|---|---|---|
@kunkunsh/core | Backend API impl, service orchestration, permission composition | No |
@kunkunsh/db | Drizzle schema, migrations, portable queries, client factory | No |
@kunkunsh/sdk | Plugin-facing SDK, manifest schema, HostAPI contracts, runtime bootstraps | No |
@kunkunsh/plugin-runtime | Electron-free plugin runtime primitives + permission gating | No |
@kunkunsh/agent | Portable AI agent loop + provider system | No (external APIs only) |
apps/desktop | Electron shell, SvelteKit renderer, native adapters, transport wiring | Yes — only here |
apps/cli | CLI/TUI shell, Effect composition root, Hono/WebSocket host | No |
Caller identity, not implicit trust
Every RPC connection or in-process caller carries a session describing who is calling:
interface BackendSession {
sessionId: string
callerKind: "trusted-shell" | "plugin-view" | "plugin-worker" | "url-view" | "cli" | "browser-ui"
installationId?: string
extensionIdentifier?: string
commandName?: string
permissionScope: PermissionScope
transport: KunkunTransportDescriptor
}Two rules follow, and both matter to extension authors:
- Permission checks run on every sensitive call, using this session — not only at connection setup.
- Plugin identity is host-created and never trusted from plugin code. The host binds the kkrpc listener to your window/worker and constructs your
HostAPIfrom trusted state. You cannot spoof another extension's identifier to reach its storage or permissions.
Not one API — scoped views
Different callers get different, scoped surfaces built from the same shared implementation modules:
| API view | Caller | Surface | Enforcement |
|---|---|---|---|
KunkunShellAPI | Trusted shell (Electron/CLI/browser UI) | Broad | Capability-checked |
KunkunHostAPI | Plugin view / worker / backend | Scoped by manifest + dynamic grants | Full permission enforcement |
KunkunUrlViewAPI | URL-backed pages | Very narrow, default-disabled | Default-deny |
A clipboard operation, for example, lives once in a shared clipboard-ops module. The shell API calls it directly; the plugin HostAPI wraps it with a clipboard-read permission check before delegating to the same module.
What this means for you
- You import
@kunkunsh/sdkand call typed methods. Under the hood those calls are kkrpc messages to the host. - You declare permissions in your manifest; the host enforces them.
- Because the core is portable, extensions that don't hard-depend on a specific shell (e.g. worker-view UIs, services, headless commands) can run across hosts — and the most ambitious extensions ship as both a CLI and a plugin.
Knowledge Graph
Every doc page and every cross-reference between them, visualised as an interactive, force-directed graph. Drag nodes, zoom, and click any page to navigate.
Next: How Extensions Run — the kkrpc + HostAPI mechanics.