Composition root
The composition root assembles all arka-deck dependencies into a living graph. It instantiates adapters, use-cases, addons, and wires them via manual dependency injection.
Source of truth: composition/core-container.ts, composition/web-container.ts.
Two containers
Section titled “Two containers”| Container | Role |
|---|---|
createCoreContainer(deps) | Instantiates the core: event bus, outbound adapters (stores, clients), inbound use-cases, first-party addons |
createWebContainer(coreContainer, deps) | Instantiates the web server layer: Fastify, routes, current UI state (workspace + project) |
The coreContainer is usable without web server (e.g. in CLI or tests). The webContainer adds the HTTP transport layer.
Instantiation order (simplified)
Section titled “Instantiation order (simplified)”1. Infrastructure adapters ├── Clock (FsClock) ├── IdGenerator (UuidIdGenerator) ├── Filesystem (FsFilesystem with allowlist) ├── ArkaHome (~/.arka-deck/ or ARKA_DECK_HOME) └── SecretCipher (AesSecretCipher)
2. Event bus └── InMemoryEventBus
3. Production stores ├── FsProjectStore, FsProjectIndexStore, FsWorkspaceStore ├── SqliteChatSessionStore ├── SqliteArkadocStore, SqliteArkadocManifestStore └── SqliteConnectorInstallationStore, etc.
4. Cortex HTTP clients ├── HttpCatalogueClient, HttpCatalogueBlocsClient ├── HttpArkadocCortexClient, HttpAtomsClient └── HttpCortexLiteRuntimeContextClient
5. Provider hub └── HttpProviderHub (registry + LLM provider lifecycle)
6. Inbound use-cases ├── buildForProjects, buildForWorkspaces ├── buildForChat, buildForCatalogue ├── buildForArkadoc, buildForSquads, buildForMissionGuardian └── ... (all implemented for-*.ts)
7. First-party addons ├── registerCortexActionsAddon ├── registerCortexLiteAddon ├── registerMemoryLocalAddon ├── registerGouvernanceLiteAddon ├── registerSquadLeaderAddon ├── registerSquadOrchestrationAddon └── registerNotionConnectAddon
8. Materializers ├── ClaudeAgentWorkspaceMaterializer ├── HookWorkspaceMaterializer └── SkillWorkspaceMaterializerComposition root import rules
Section titled “Composition root import rules”composition/ is the only folder allowed to import simultaneously from:
core/(ports, use-cases, domain)adapters/(outbound + inbound)addons/(registers)providers/(runtimes)
Other folders strictly respect the boundaries (see ADR 0001).
Wiring an addon
Section titled “Wiring an addon”The typical pattern to wire an addon in composition:
// composition/core-container.ts (simplified extract)
import { registerCortexActionsAddon } from '../addons/cortex-actions/src/index.js';
export async function createCoreContainer(deps): Promise<CoreContainer> { // ... infrastructure, event bus, stores
const cortexActionsRuntime = registerCortexActionsAddon({ clock, filesystem, eventBus, idGenerator, resolveSession: (sessionId) => chatSessionStore.getSession(sessionId).then((s) => s?.projectPath ?? null), cortexBaseUrl: process.env.ARKA_DECK_CORTEX_URL, });
// Register the runtime in the addon registry addonRegistry.registerRuntime('cortex-actions', cortexActionsRuntime);
return { /* ... */ };}If the addon exposes HTTP routes, their wiring lives in composition/addons/<name>-routes.ts and is called from web-container.ts.
Lifecycle
Section titled “Lifecycle”| Phase | Action |
|---|---|
| Boot | createCoreContainer instantiates the graph, starts bus subscriptions |
| Run | Routes consume use-cases via the container |
| Shutdown | coreContainer.dispose() calls addon unsubscribe() and closes resources (SQLite, sidecars) |
See also
Section titled “See also”- Overview: ./overview
- Inbound ports: ./ports-inbound
- Outbound ports: ./ports-outbound
- Materializers: ./materializers