Skip to content

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.


ContainerRole
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.


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
└── SkillWorkspaceMaterializer

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).


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.


PhaseAction
BootcreateCoreContainer instantiates the graph, starts bus subscriptions
RunRoutes consume use-cases via the container
ShutdowncoreContainer.dispose() calls addon unsubscribe() and closes resources (SQLite, sidecars)