Architecture — overview
Guide for developers who want to understand arka-deck’s code organization, dependency rules and functional clusters.
Overview
Section titled “Overview”arka-deck is a hexagonal TypeScript/Node.js application. The core depends on nothing external — all dependencies point towards it, never the other way around.
┌─────────────────────────────────────────────────────────────┐│ adapters/ ││ ┌──────────────────┐ ┌──────────────────────────┐ ││ │ inbound/ │ │ outbound/ │ ││ │ cli/ │ │ filesystem/ │ ││ │ web/ │ │ http/ │ ││ │ server/ │ │ chat/ │ ││ │ ui/ (React) │ │ providers/ │ ││ └──────────────────┘ │ events/ │ ││ │ process/ │ ││ │ secrets/ │ ││ │ system/ │ ││ └──────────────────────────┘ ││ ┌─────────────────────────────────────────┐ ││ │ composition/ │ ││ │ core-container.ts web-container.ts │ ││ └─────────────────────────────────────────┘ ││ ┌─────────────────────────────────────────┐ ││ │ core/ │ ││ │ domain/ ports/ use-cases/ │ ││ │ orchestration/ │ ││ └─────────────────────────────────────────┘ ││ ┌─────────────┐ ┌──────────────────────┐ ││ │ addons/ │ │ providers/ │ ││ │ workers/ │ │ claude_code/ │ ││ └─────────────┘ │ codex_cli/ │ ││ │ google-genai/ │ ││ └──────────────────────┘ │└─────────────────────────────────────────────────────────────┘Root folders
Section titled “Root folders”| Folder | Role |
|---|---|
core/ | Pure domain — ports, use-cases, domain types, orchestration |
adapters/inbound/ | Entry points — CLI and web server (Fastify + React/Vite UI) |
adapters/outbound/ | I/O implementations — filesystem, HTTP, chat, providers, secrets, system |
composition/ | Dependency wiring — manual DI, containers |
addons/ | Self-contained event-driven modules — pre-installed first-party addons |
providers/ | LLM runtime implementations — claude_code, codex_cli, google-genai |
workers/ | 1-shot headless workers — manifest + prompt |
bin/ | arka-deck CLI binary |
scripts/ | Utility scripts (release, prepare-dist, CI gates) |
e2e/ | Playwright end-to-end tests |
The hexagonal core. No dependency to adapters/, addons/, composition/ or providers/.
core/├── domain/ # Pure types and business rules│ ├── chat/ # Transcript, StreamEvent, sessions│ ├── catalogue/ # Profiles, installs, agents│ ├── events/ # ArkaEvent — discriminated union of all bus events│ ├── project/ # Project, workspace│ ├── providers/ # Provider manifests│ ├── squad/ # Squad composition│ └── workers/ # Worker manifests├── ports/│ ├── inbound/ # Use-case contracts (for-chat.ts, for-projects.ts…)│ └── outbound/ # I/O contracts (event-bus.ts, filesystem.ts, clock.ts…)├── use-cases/ # Application logic — implements inbound ports└── orchestration/ # Multi-agent orchestration (LangGraph)Inbound ports
Section titled “Inbound ports”Inbound ports (core/ports/inbound/for-*.ts) define the contracts that use-cases expose to adapters:
for-chat.ts— start, resume, send a turnfor-projects.ts— projects and workspaces CRUDfor-catalogue.ts— load and install agents from Cortexfor-workers.ts— trigger a workerfor-preferences.ts— read and write preferences- and several more — the exhaustive list is in
ports-inbound.md(coming).
Outbound ports
Section titled “Outbound ports”Outbound ports (core/ports/outbound/*.ts) define the contracts that adapters implement:
event-bus.ts— typed in-process event busfilesystem.ts— file read/write (with allowlist)chat-runtime.ts— LLM turn executionbefore-turn-augmenter.ts— context injection before a turnclock.ts,id-generator.ts— utilities
Import rules (ESLint enforced)
Section titled “Import rules (ESLint enforced)”Boundaries are enforced mechanically by ESLint on each lint:
| Source | Forbidden to import |
|---|---|
core/** | adapters/**, composition/**, addons/**, providers/** |
core/domain/** | core/use-cases/** |
adapters/inbound/** | adapters/outbound/** |
adapters/** | addons/** |
Composition (composition/) is the only place allowed to wire everything together.
composition/
Section titled “composition/”The composition container instantiates all adapters, use-cases and addons, then wires them via manual dependency injection.
core-container.ts— instantiates the core (event bus, stores, use-cases)web-container.ts— adds the Fastify server, routes and UI
It is the only file allowed to import from addons/, providers/ and adapters/ simultaneously.
Event bus
Section titled “Event bus”The bus is a core module (core/ports/outbound/event-bus.ts), instantiated once at boot in core-container.ts. It carries typed in-process events across all modules.
interface EventBus { publish(event: ArkaEvent): Promise<void>; // awaits all handlers publishAsync(event: ArkaEvent): void; // non-blocking subscribe<T extends ArkaEventType>( type: T, handler: EventHandler<T>, ): Unsubscribe;}The full event list is in core/domain/events/arka-event.ts (source of truth). Naming convention: <domain>.<entity>.<action> kebab-case. See event-bus for full documentation.
addons/
Section titled “addons/”Each addon is a self-contained module in its own subfolder:
addons/<name>/├── manifest.json # declarative metadata└── src/ ├── index.ts # register function + public re-exports ├── domain/ # addon-local domain types ├── ports/ │ ├── for-<name>.ts # addon-owned inbound port │ └── *.ts # outbound ports (stores, HTTP clients) ├── adapters/ # outbound port implementations └── use-cases/ # addon application logicAddons can import from core/ (outbound ports, domain types) but not from adapters/ nor from other addons.
To create an addon, see the ../extension/ecrire-un-addon.md tutorial (coming).
providers/
Section titled “providers/”Providers implement the chat-runtime.ts outbound port for a given LLM:
providers/├── claude_code/ # @anthropic-ai/claude-agent-sdk SDK├── codex_cli/ # Codex CLI└── google-genai/ # Google GeminiEach provider exposes a runtime.ts that satisfies the ChatRuntime contract and a manifest.json or equivalent declaring its metadata (available models, capabilities).
workers/
Section titled “workers/”Workers are headless LLM processes declared by manifest.json. They are not executed directly — arka-deck’s worker system invokes them through the for-workers.ts port.
workers/<name>/├── manifest.json # declaration (input, output, runtime, prompt_source)├── prompt.json # local prompt (optional — otherwise via Cortex)└──To design a worker, see ../extension/ecrire-un-worker.md (coming).
Architecture Decision Records (ADRs)
Section titled “Architecture Decision Records (ADRs)”Structural decisions are documented in ../../adr/:
| ADR | Topic |
|---|---|
| 0001 | Hexagonal architecture |
| 0002 | Addon contract |
| 0003 | Materializer pattern |
| 0004 | GitHub-only distribution |
| 0005 | Local AES-GCM secrets |
| 0006 | Anti-SSRF provider protection |
Technical stack
Section titled “Technical stack”| Layer | Technology |
|---|---|
| Runtime | Node.js ≥ 20.19.0, TypeScript (strict) |
| HTTP server | Fastify 5.8.5 |
| UI | React 18, Vite, Tailwind CSS, React Query |
| Local storage | SQLite (better-sqlite3), JSON files |
| Secrets | AES-256-GCM under ~/.arka-deck/ |
| Validation | Zod |
| Claude SDK | @anthropic-ai/claude-agent-sdk 0.2.129 |
| Orchestration | LangChain / LangGraph |
| Tests | Vitest (unit), Playwright (E2E) |
| Linting | ESLint (max-warnings=0), Prettier |