Architecture
How DorkOS connects your browser to Claude Code using a hexagonal architecture
Architecture
DorkOS sits between you and Claude Code, providing a browser-based command center on top of the Claude Agent SDK. Understanding how the pieces fit together helps you troubleshoot issues, choose the right deployment mode, and build integrations.
The Big Picture
DorkOS uses a hexagonal architecture (also called ports and adapters) to keep the UI completely separate from how it communicates with your agent backend. The same React client works in two very different environments without any code changes.
You (browser)
|
v
DorkOS Client (React SPA)
|
v
Transport (interface)
|
+---> HttpTransport ---> DorkOS Server (Express) ---> AgentRuntime ---> Claude Agent SDK ---> Claude API
|
+---> DirectTransport ---> AgentRuntime ---> Claude Agent SDK ---> Claude API (Obsidian plugin, no server)Two abstractions make this work. The Transport interface decouples the React client from its backend — the client calls transport.sendMessage() and never knows whether events came from an SSE stream or an in-process generator. The AgentRuntime interface decouples the server routes from specific agent backend implementations — routes call runtimeRegistry.getDefault() and never reference ClaudeCodeRuntime directly.
Two Deployment Modes
Standalone Web
The default mode. DorkOS runs as an Express server, and the React client communicates with it over HTTP and Server-Sent Events.
- The client sends REST requests and receives streamed responses via SSE
- The server manages sessions and reads transcript files from disk
- Works with any modern browser, no plugins required
- Supports multiple simultaneous clients viewing the same session
Obsidian Plugin
DorkOS also runs as an Obsidian plugin, embedded directly inside the note-taking app. There is no separate server process.
- DirectTransport calls service instances in the same Electron process
- No HTTP, no port binding, no network traffic
- Sessions are still stored as JSONL files in the same location, so CLI-started sessions are visible too
Both modes read from the same session storage on disk. A session started with the Claude Code CLI appears in DorkOS, and vice versa.
How a Message Flows
When you type a message and press Enter:
- ChatPanel captures your input and calls
transport.sendMessage() - The Transport adapter delivers the message to the backend (via HTTP or directly)
- The server calls
runtimeRegistry.getDefault()to get the activeAgentRuntime - The AgentRuntime calls the Claude Agent SDK and begins streaming a response
- Stream events flow back through the transport as callbacks: text deltas, tool calls, approvals, and the final result
- The React client updates the UI in real time as each event arrives
This flow is identical regardless of which transport adapter or runtime is active.
Key Components
AgentRuntime and RuntimeRegistry
The AgentRuntime interface (@dorkos/shared/agent-runtime) is the contract that all agent backends implement. It covers session lifecycle, message streaming, tool approval, storage queries, session locking, and capability reporting. Every route obtains the active runtime through runtimeRegistry.getDefault() — never by referencing a specific implementation.
The RuntimeRegistry holds all registered runtimes keyed by type string. The current default implementation is ClaudeCodeRuntime, which integrates the Claude Agent SDK. Future runtimes (OpenCode, Aider, etc.) can be registered without touching route code.
Server
The Express server exposes route groups including sessions, commands, health, config, files, git, Tasks scheduling, Relay messaging, Mesh agent discovery, and extensions. Routes are thin HTTP handlers — all business logic delegates to services or the active runtime.
Server-side services are organized by domain: services/core/ (shared infra), services/runtimes/ (agent backends), services/tasks/ (scheduling), services/relay/ (messaging), services/mesh/ (discovery), services/discovery/ (filesystem scanning), services/session/ (session management), services/extensions/ (extension lifecycle, compilation, and discovery), services/marketplace/ (package install/uninstall/update pipeline, transaction engine, cache), and services/builtin-extensions/ (always-on extensions like Dork Hub, auto-staged at startup).
Client
The React SPA uses Feature-Sliced Design with strict unidirectional layer imports. Key features:
- Chat — Message list, streaming text, tool call cards, and the input area
- Session list — Sidebar for browsing and creating sessions
- Canvas — Agent-controlled UI panels rendered via the extension system
- Commands — Palette for discovering and using slash commands
- Settings — Theme, configuration, and preferences
- Tasks / Relay / Mesh — Scheduling, messaging, and agent discovery panels
Agent UI Control
Agents can observe and control the DorkOS client UI through MCP tools exposed by the server. The controlUI tool dispatches UI commands (open panels, navigate, show notifications), and get_ui_state returns the current UI state (active session, visible panels, sidebar state). This enables agents to build interactive workflows that coordinate with the user's view. UI state is passed to the agent via the uiState field on sendMessage.
Shared Package
Types, Zod schemas, the Transport interface, and the AgentRuntime interface all live in @dorkos/shared. Both client and server import from this package to stay in sync.
Session Storage
All session data lives in SDK transcript files on disk at ~/.claude/projects/{project-slug}/. DorkOS does not maintain a separate session database. The TranscriptReader scans JSONL files to build session lists, extract metadata, and replay message history.
This means:
- Sessions persist across DorkOS restarts
- Sessions created by the CLI or other tools are automatically visible
- There is no delete operation — sessions exist as long as their transcript files do
Transcript files can grow large for long conversations. DorkOS reads them incrementally where possible, but very long sessions may take a moment to load.