The desktop client currently mixes three kinds of responsibilities:
- workspace UI: asset tree, tabs, connection entry, status display
- platform shell: Electron windowing, local app launch, native dialogs
- protocol runtime: built-in SSH bridge and future potential database protocol bridges
At the same time:
kokoalready provides protocol proxy, websocket terminal, ACL, auditing, and multi-protocol session handlinglunaalready provides a web workspace model based on asset tree plus tabbed connectors- the desktop client is growing toward the same product shape as
luna
If we continue embedding protocol libraries directly into the client for SSH, MySQL, PostgreSQL, Redis, and others, the client will become larger, harder to maintain, and will drift from the web terminal behavior.
This document defines a unification plan: protocol handling converges into koko, while desktop and web converge into one shared Vue workspace.
Current implementation note: iframe-based session surfaces have been removed from the shared workspace. Later references to embedding describe historical migration options, not requirements for pane layout or session lifecycle.
- stop expanding desktop-native protocol implementations beyond narrow platform-specific needs
- make
kokothe default runtime for terminal and database session protocols - converge desktop and web onto one shared workspace UI implementation
- preserve the desktop shell advantages: local app launch, system integration, native packaging
- reduce duplicate logic across
clients,luna, andkoko
- dropdown, popover, and modal typography should stay aligned with the page's primary text size instead of appearing larger than the surrounding UI
- menus should prefer compact vertical spacing by default so utility operations feel lightweight and scan quickly
- toolbar overflow actions should sit adjacent to the control they extend; for example, a section-management
...belongs next to search when it configures the same header area - theme implementation should follow a three-layer model: preset seed tokens, semantic app tokens, and component consumption
- Nuxt UI based screens should consume Nuxt UI components first and avoid custom theme branching unless a component is not expressive enough
- custom connector/workspace UIs must not infer colors from
primaryor a single background color; they should only consume semantic tokens such as surface, text, border, hover, selected, and focus
useMobile()shares the reactive layout mode;app.vuepublishes it asbody.mobile.- Mobile layout requires a coarse primary pointer with no hover and a viewport width of at most 767px or height of at most 600px. Desktop resizing alone does not activate it.
- Use
body.mobilefor mobile styles, including scoped component styles and teleported content; useuseMobile()for UI behavior. Width-only responsive rules may still adapt constrained desktop layouts. - Session and nested tab strips use
--workspace-session-tab-widthand--workspace-sub-tab-widthfor both their ideal strip width and each tab's preferred width, keeping the create control next to the last tab.
The shared workspace should use one theme pipeline for desktop shell UI and connector workspaces.
Theme presets define only the small set of seed tokens:
--theme-bg--theme-fg--theme-muted--theme-border--theme-accent--theme-folder-icon(optional; defaults to--theme-accent)--theme-folder-icon-filled(optional;0for outline,1for filled)--theme-surface--theme-surface-hover--theme-shadow-soft
This is the only layer that should vary between presets such as Catppuccin, Gemini, Luna, or future brand skins.
Global CSS derives semantic tokens from the seeds, for example:
- text: primary, secondary, muted, inverse
- surfaces: canvas, sidebar, panel, header, footer, input, card, overlay
- interaction: hover, selected, focus ring
- borders: subtle, strong
Connector authors should treat these semantic tokens as the stable contract.
Additional domain tokens should be defined on top of the semantic layer:
editor.*for CodeMirror 6, SQL Editor, diff editor, and futurecheneditor surfacessyntax.*for code highlighting shared by file editing and SQL editingterminal.*for xterm-based surfacesdataGrid.*for SQL results, schema tables, and tabular inspectorsworkspace.*for connector-owned shells such as file manager, k8s UI, or future multi-pane tools
- Nuxt UI components should pick up the theme through global UI variables and app config overrides.
- bespoke widgets such as xterm, CodeMirror, iframe shells, and file trees should read semantic tokens only.
- workspace code should never hardcode white/black backgrounds for protocol surfaces unless the protocol runtime requires it and the value is still derived from semantic tokens.
We should treat Zed as an inspiration and an import target, not as the sole source of truth.
- our schema should remain domain-oriented around shared UI, editors, terminals, tables, and workspaces
- Zed themes can be imported into a compatible subset by mapping Zed theme fields into our
seed,editor,syntax, andterminaldomains - unsupported Zed-only fields such as product-specific chrome or unsupported syntax scopes may be ignored during import
- custom product-specific domains such as
workspace.*or future connector-specific panels remain first-class in our schema
- rewrite all of
kokofrontend pages in one step - replace every existing Luna feature before starting migration
- remove native external application launch support
- unify every product frontend into one repo immediately
- built with Electron + Vue/Nuxt
- already has asset tree and tabbed workspace behavior
- provides external-terminal SSH through a Node helper running on Electron's bundled Node runtime
- plugin system is being introduced for external applications
- already exposes authenticated web routes such as
/koko/connect/ - already exposes websocket terminal endpoints under
/koko/ws/terminal - already contains multi-protocol server connection code in
pkg/srvconn - already owns session policy, ACL, auditing, and protocol-specific runtime behavior
- built with Angular
- already models the product as a workspace shell with left asset tree and right tab area
- already uses iframe-based connector loading for web terminal and other connector pages
Use koko as the session and protocol gateway.
Build a shared Vue workspace that can run in:
- desktop: inside the Electron shell
- web: as the browser workspace replacing Luna incrementally
Keep native local-app launch as a platform adapter, not as the main connection implementation.
flowchart LR
subgraph shell ["Platform Shell"]
Desktop["Electron Desktop Shell"]
Browser["Web Browser Shell"]
end
subgraph workspace ["Shared Vue Workspace"]
AssetTree["Asset Tree"]
Tabs["Tabs / Layout"]
ConnectFlow["Connect Flow"]
Adapter["Connector Adapter Layer"]
end
subgraph connectors ["Connector Implementations"]
KokoView["Koko Web Connector"]
NativeLaunch["Native App Launcher"]
BuiltinExp["Optional Builtin Experimental Connector"]
end
subgraph backend ["Backend Runtime"]
Koko["Koko Session Gateway"]
JMS["JumpServer APIs"]
end
Desktop --> workspace
Browser --> workspace
AssetTree --> ConnectFlow
Tabs --> Adapter
ConnectFlow --> Adapter
Adapter --> KokoView
Adapter --> NativeLaunch
Adapter --> BuiltinExp
KokoView --> Koko
NativeLaunch --> JMS
Koko --> JMS
Default protocols should run through koko whenever possible:
- SSH
- Telnet
- database protocols such as MySQL, PostgreSQL, Redis, MongoDB, Oracle, SQL Server
- Kubernetes terminal-style sessions
- SFTP and similar web-managed sessions when supported
Benefits:
- one place for protocol maintenance
- consistent ACL and audit behavior with web
- less client binary growth
- fewer per-platform protocol bugs
The desktop app should focus on:
- authentication bootstrap
- workspace rendering
- local app launching
- OS integration
- settings, notifications, storage
It should not become the long-term home for protocol client stacks.
The asset tree, tabs, connect dialog, workspace status, and view lifecycle should exist once in a shared Vue implementation.
This shared workspace should replace:
- the new duplicated workspace logic in
clients - the Angular workspace shell in
luna
The shared workspace session path uses native Vue connector surfaces. Split-pane drag and drop, layout changes, and session lifecycle preservation assume that surfaces live in the same Vue document.
Iframe-specific drag overlays, hit testing, and lifecycle workarounds are outside the workspace design. If external web content is introduced again, its connector adapter must own that boundary without adding iframe branches to shared pane layout code.
Native Vue connector surfaces avoid an extra document boundary. Their dominant latency is usually:
- user input event handling
- websocket round-trip
- remote target response
- terminal rendering
The practical workspace integration issues are:
- focus handoff
- keyboard shortcut routing
- copy/paste behavior
- drag/drop and upload
- theme synchronization
- tab title/status synchronization
- session close and reconnect signaling
Pane moves and layout changes must preserve the mounted connector instance so they do not close sockets, clear terminal state, or reconnect a session.
This is a key design point.
Current koko/connect routes are authenticated by session middleware. Relying on browser cookies alone is fragile for desktop embedding.
We should introduce a controlled bootstrap model for embedded connectors.
The desktop and future shared web workspace need a stable way to open a koko connector view using an explicit short-lived authorization mechanism.
- User authenticates in the shell application.
- Workspace requests a short-lived embed authorization from JumpServer.
- Shell opens the
kokoconnector URL with that authorization. kokovalidates the authorization and establishes session state for the connector page and websocket.
- avoids hidden cookie coupling
- improves desktop reliability
- makes connector embedding more portable
- makes future web/desktop sharing cleaner
Exact token form can be decided later, but it should be:
- short-lived
- scoped to user and target connection context
- usable by both HTTP page bootstrap and websocket upgrade
- auditable
We have validated that embedding koko inside the desktop client through an iframe or webview is feasible from a UI perspective, but the current authentication model blocks the desktop flow.
Current behavior:
kokoHTTP middleware only checks browser cookies- unauthenticated requests are redirected with HTTP
302 - desktop client currently authenticates backend API calls with bearer token plus org context, not with browser cookie state
This means the desktop client should not continue depending on cookie synchronization hacks for embedded koko.
koko must support the same authenticated client session model already used by the desktop client for backend API access.
For embedded desktop access, koko authentication should support:
- existing cookie-based web authentication for browser compatibility
- bearer-based client authentication for desktop embedding
Recommended authentication order inside koko:
- try existing cookie-based authentication
- if cookie authentication is absent or invalid, try
Authorization: Bearer ... - validate bearer token against core
- resolve user and org context
- continue using the same user/session context as normal web requests
- avoids hidden dependency on browser cookie storage
- avoids coupling desktop embedding to Luna dev proxy behavior
- aligns
kokowith the desktop client's existing API authentication model - gives a cleaner base for future explicit embed auth
koko should not trust bearer strings locally without validation.
It must validate the incoming bearer token with core before treating the request as authenticated.
This support must cover both:
- HTTP page routes such as
/koko/connect/ - websocket routes such as
/koko/ws/terminal/
Once koko supports bearer-based authentication:
- desktop can embed
kokowithout requiring cookie injection - iframe/webview requests must attach bearer and org context explicitly
- later optimization can move from iframe reuse to a lighter connector shell without changing the authentication model
We should split the future shared workspace into three layers.
Shared domain and orchestration logic:
- asset and protocol models
- connection session models
- tab lifecycle
- organization and site context
- permission-aware connection flow
- event contracts between workspace and connector adapters
Shared Vue UI implementation:
- asset sidebar
- search and filters
- tabs and split layout
- connection dialog
- empty states and status toasts
- top workspace chrome
Connector runtime abstraction:
koko-web-adapternative-app-adapterbuiltin-terminal-adapteras experimental or fallback only
Suggested connector adapter interface:
export interface WorkspaceConnectorAdapter {
kind: "koko-web" | "native-app" | "builtin-terminal";
supports: (protocol: string, connectMethod: string) => boolean;
open: (session: WorkspaceSession) => Promise<WorkspaceViewHandle>;
focus: (viewId: string) => void;
resize: (viewId: string, rect: { width: number; height: number }) => void;
close: (viewId: string) => Promise<void>;
}To keep future workspace growth maintainable, every connector component should declare its supported:
- component identity
- protocols
- connect methods
- workspace surfaces
This declaration should be a source of truth consumed by:
- connect method normalization and presentation
- workspace surface routing
- default method selection
- future capability inspection or admin diagnostics
Example declaration shape:
export interface WorkspaceCapabilityDeclaration {
component: "koko" | "chen" | "lion" | "tinker";
surface: "terminal" | "file-manager" | "file-editor" | "k8s-ui";
protocols: string[];
connectMethods: string[];
backendConnectMethod?: string;
}Current known koko declarations:
- built-in terminal:
ssh,telnet,mysql,mariadb,postgresql,redis,mongodb,oracle,sqlserver - file manager:
sftp - file editor:
sftp - Kubernetes UI:
k8s
Rule of thumb:
- Protocol support belongs to the component declaration, not scattered
if/else. - A connect method is a user-visible entry choice.
- A workspace surface is the actual UI/runtime implementation opened by that choice.
- Multiple workspace surfaces may share one backend connect method, such as SFTP file manager and file editor.
To make component-owned workspaces obvious and maintainable, each connector component should keep its workspace implementations in a dedicated workspaces/ directory under its own module root.
Examples:
ui/koko/workspaces/ui/chen/workspaces/ui/lion/workspaces/
Current koko workspace files should stay explicit and one-to-one with the user-visible workspace types:
ui/koko/workspaces/TerminalSessionSurface.vueui/koko/workspaces/FileManagerSessionSurface.vueui/koko/workspaces/FileEditorSessionSurface.vueui/koko/workspaces/KubernetesWorkspace.vue
Shared base abstractions are encouraged so workspace implementations do not duplicate session bootstrap logic.
Recommended pattern:
- keep one file per user-visible workspace
- extract shared session bootstrap into a base composable such as
useBaseWorkspaceSession - extract shared ready/loading/error shell into a base component such as
BaseWorkspaceShell - let component-specific workspaces compose or extend those base pieces instead of reimplementing endpoint lookup, ticket exchange, theme sync, and common state handling
This pattern should apply not only to koko, but also to future component modules such as chen and lion.
Rules:
- All workspace UI implementations for a component should live in that component's
workspaces/directory. - Cross-component routing code may import from those directories, but should not redefine the implementations elsewhere.
- New workspace types should be added there first, then declared in the component capability registry.
- Common workspace behavior should be abstracted into reusable base capabilities when multiple workspaces share the same lifecycle.
- This keeps protocol declaration, connection method mapping, and workspace implementation organization aligned.
Status:
- document the target architecture
- stop adding new first-class built-in protocol stacks without explicit exception
- keep current built-in SSH path as temporary and experimental
Deliverables:
- this design document
- architecture decision communicated across
clients,koko, andluna
Goal:
Introduce a new connector path in the desktop client that opens koko connector views instead of the desktop SSH helper.
Scope:
- add
koko-webconnector adapter - open
koko/connectviews in the right-side workspace - pass asset/session context through the new adapter flow
- synchronize tab close, focus, title, and terminal-ready events
- preserve current native-app launch path for external apps
Deliverables:
- SSH can run through
kokoin desktop - current built-in SSH is no longer the default
Exit criteria:
- desktop SSH via
kokoreaches acceptable usability - no new desktop-native protocol bridge work is started for databases
Goal:
Replace implicit cookie dependence with a desktop-compatible authenticated embed mechanism.
Scope:
- add bearer-compatible authentication support to
koko - support desktop bootstrap and websocket authentication without relying on browser cookie state
- optionally evolve later into a short-lived embed authorization contract
- support future browser workspace use
Deliverables:
- desktop connector loading does not depend on manually synchronized cookie state
- connector boot is observable and debuggable
Exit criteria:
- connector auth failures can be traced clearly
- same mechanism works in desktop and browser shells
Goal:
Make the current desktop workspace implementation reusable outside Electron.
Scope:
- extract workspace domain model
- extract reusable Vue UI modules
- isolate Electron-only code behind platform service interfaces
Deliverables:
- shared workspace packages or workspace modules
- desktop app consumes the shared modules
Exit criteria:
- asset tree, tabs, and connect flow no longer depend directly on Electron APIs
Goal:
Incrementally replace Luna's Angular shell with the shared Vue workspace.
Scope:
- first replace the main asset-tree plus tabbed workspace shell
- keep existing connector pages if needed during transition
- migrate per-feature rather than all-at-once
Suggested migration order:
- asset tree and navigation shell
- tab workspace and view lifecycle
- connect dialog and connect method selection
- session tabs using
koko-webadapter - remaining Luna-only integrations
Deliverables:
- Vue workspace runs in browser
- Angular Luna surface starts shrinking instead of growing
Exit criteria:
- core workspace path no longer requires Angular
Goal:
Move from page-level iframe reuse toward a more reusable connector contract where justified.
Scope:
- evaluate whether
kokofrontend logic should expose a reusable frontend SDK or more structured embed contract - only do this after Phases 1 to 4 are stable
Deliverables:
- cleaner connector integration where needed
- reduced iframe boundary friction over time
Will own:
- Electron shell
- local app launching
- platform integrations
- shared Vue workspace consumption
Will reduce ownership of:
- built-in protocol runtimes
- protocol-specific terminal behavior
Will own:
- session bootstrap
- websocket terminal runtime
- protocol adapters
- policy and audit behavior
- reusable embedded connector contract
Needs enhancement in:
- embed-friendly auth bootstrap
- desktop/webview integration hooks
- postMessage or host bridge contract if iframe embedding continues
Will move toward:
- consumer or host of the shared Vue workspace
- eventual retirement of duplicated Angular workspace shell
Will gradually reduce:
- duplicated tabbed workspace implementation
- duplicated connection shell behavior
Mitigation:
- treat embedded focus and keyboard handling as first-class integration work
- keep native app launch for protocols or workflows where embedded UX is not ideal
Mitigation:
- prioritize explicit embed bootstrap in Phase 2
- avoid long-term reliance on ambient browser cookie state
Mitigation:
- freeze major new workspace features in duplicated shells where possible
- build new workspace features in shared Vue modules only
Mitigation:
- accept page embedding only as a transition step
- revisit structured connector contracts after shared workspace unification
When adding or changing connection features:
- If
kokocan own the runtime, preferkoko. - If the feature is UI shell behavior, build it in the shared Vue workspace.
- If the feature is OS-specific, build it in the desktop shell adapter.
- Only add built-in protocol runtime inside
clientswith explicit justification.
Tasks:
- add a
koko-webconnection adapter inclients - wire workspace tabs to host a connector WebView or iframe-like container
- route SSH built-in connect flow to the
koko-webadapter by default - keep the Node external-terminal SSH helper as a fallback path
Tasks:
- add bearer-aware auth middleware support in
koko - define the desktop-to-
kokorequest contract - pass org context together with authenticated desktop requests
- make websocket authentication work under the same model
Suggested Koko work items:
- extend
HTTPMiddleSessionAuthto support bearer fallback after cookie check - add the same fallback logic for websocket authentication entry points
- validate bearer token through core before creating request user context
- ensure org context is honored consistently with desktop API session behavior
Suggested desktop work items:
- define how embedded requests attach bearer credentials
- define how org context is attached to embedded requests
- keep the current iframe experiment only as a temporary harness until bearer auth lands in
koko
Tasks:
- identify Electron-bound code in the current Vue workspace
- extract pure workspace state and tab logic
- define platform services for shell-only features
Tasks:
- mount the shared Vue workspace in a browser target
- run one end-to-end asset-to-SSH connection flow
- validate parity expectations against Luna
- should the shared workspace live in this repo first, or in a new shared workspace repo
- should desktop embed
kokothrough a standard webview route, or through a more specialized internal browser component - should
kokoexpose postMessage hooks for host control, or should the host rely only on URL plus websocket behavior - what is the minimum viable embed auth contract for Phase 2
- which Luna features are truly core for first migration, and which can wait
Start with Milestone A.
That gives the team the fastest architectural leverage:
- immediate reuse of
koko - no further database protocol pressure on the desktop client
- a practical base for shared workspace evolution