본문으로 건너뛰기

메인 프로세스 설계

메인 프로세스는 Elftia의 핵심으로, 모든 비즈니스 로직, 외부 API 호출, 데이터베이스 작업 및 보안 제어를 담당합니다. 진입 파일은 packages/desktop/app/main/index.ts입니다.

서비스 모듈 개요

메인 프로세스에는 45개 이상의 서비스 모듈이 포함되어 있으며, packages/desktop/app/main/services/ 하위에 기능 도메인별로 구성되어 있습니다:

graph LR
subgraph "Core Infrastructure"
Core[core/]
Config[config/]
Settings[settings/]
end

subgraph "Auth & Accounts"
Auth[auth/]
Account[account/]
OAuth[oauth/]
end

subgraph "AI Engines"
Engine[engine/]
Completion[completion/]
LLM[llm/]
Prompt[prompt/]
end

subgraph "Agent Orchestration"
Magi[magi/]
Agent[agent/]
Plugin[plugin/]
end

subgraph "Messaging Channels"
Channel[channel/]
Security[security/]
end

subgraph "Media & Content"
Media[media/]
Project[project/]
Search[search/]
end

Core --> Engine
Core --> Channel
Engine --> Magi
LLM --> Completion
Completion --> Engine
Magi --> Agent
Channel --> Security

도메인별 서비스 목록

도메인서비스설명
Core (core/)LoggerServiceWinston + daily-rotate-file 로깅
AppPaths애플리케이션 경로 확인 (userData, dbPath, configDir 등)
RuntimeService런타임 정보 (버전, 채널, 플랫폼)
CacheService범용 캐시 (TTL 만료 정리)
ConfigManager설정 파일 관리
SecurityServiceAES-256-GCM 암호화 서비스
CryptoServicePBKDF2 키 파생 + 암호화/복호화
PerformanceMonitor성능 모니터링 및 알림
ErrorHandler전역 오류 처리
PluginManager플러그인 생명주기 관리
DatabaseOptimizerSQLite 최적화 (VACUUM, ANALYZE)
CommandService명령 실행 서비스
Auth (auth/)AuthServiceIPC 인증 토큰 유효성 검사
AuthSessionService로그인 세션 + Deep Link 처리
AuthTokenStore인증 토큰 영속성
TokenRefreshService자동 토큰 갱신
Account (account/)AccountService사용자 자격 증명 관리
AccountTokensServiceOAuth 토큰 관리
LimitsService멤버십 한도 확인
LLM (llm/)LLMConfigService공급자/모델 설정 CRUD
TransformerService요청 형식 변환 체인
Completion (completion/)CompletionServiceLLM API 호출 진입점
ApiKeyPoolService다중 키 라운드로빈 + 세션 선호도
Engine (engine/)EngineDispatcher엔진 등록 및 라우팅
ApiEngine미디어 API 엔진
ChatEngine일반 채팅 엔진
ClaudeSdkEngineClaude Agent SDK 엔진
TinyElfEngine내장 Agent 엔진
CliRunnerEngineCLI 서브프로세스 엔진
STChatEngineRP 채팅 파이프라인 엔진
Magi 오케스트레이션 (magi/)MagiService핵심 메시지 처리 서비스 (assembleMagiMcps를 통해 services/capabilities/tools/mcp-builtin/ 레지스트리로 Clawia의 MCP 집합을 수집하며, buildTinyElfDirectMcpServers는 TinyElf 진입점)
MagiSessionService세션 영속성 관리
MagiWorkspaceService작업 공간 디렉토리 관리
MagiSdkOptionsBuilder프롬프트 빌드 (V1-V4) + 사용자 MCP 주입 (setUserMcpServers / getMcpServers(allowedUserMcpNames) / getDirectMcpServers) + Channel 프로브; Phase 5.9부터 내장 MCP 어셈블리는 mcp-providers/로 이전
AgentOrchestratorAgent 디스패치 및 오케스트레이션
AgentDiscovery스케줄 가능한 Agent 목록
MessageRouter이중 모드 메시지 라우팅
ChannelMagiBridgeChannel에서 Magi로의 브릿지
SessionEventBus세션 이벤트 버스
SessionDispatcherImpl크로스 세션 디스패치 구현
SubagentRegistry하위 Agent 상태 추적
내장 MCP 레지스트리 (mcp-providers/)McpProviderRegistry12개 내장 공급자 + 동적 ScriptPlugin 공급자 팩토리. 모든 내장 MCP 핸들러는 인프로세스 실행; SDK가 직접 호출 / TinyElf가 ITool로 등록 / CLI는 중앙 BuiltinMcpHttpServer HTTP 브릿지를 통해 접근. DispatchServer / ChannelServer / VisionAssistServer HTTP 브릿지는 cleanup-legacy-mcp (2026-05-19)에서 제거됨
BuiltinMcpHttpServer중앙 HTTP MCP 브릿지 (세션 범위); 모든 인프로세스 MCP를 CLI 엔진에 노출
assembleMcpForSession(ctx)통합 어셈블리 진입점 (SDK + TinyElf; sdkServers / tinyElfServers / promptFragments / allowedTools / cleanups를 포함하는 McpAssembly 반환)
applyAssembly.tsapplyAssemblyToSdkOptions / applyAssemblyToTinyElfConfigMcpAssembly를 엔진 옵션에 병합
contextBuilders.tsbuildAssemblyContextFor{Clawia,Agent} — 호출자 관점에서 AssemblyContext 구성
registerBuiltinMcpProviders.ts일회성 부트 어셈블리 포인트 (ScriptPlugin 동일 이름 별칭 처리 포함)
builtin/<Name>Provider.ts내장 MCP별 Provider 모듈 (정적 12개 + 동적 ScriptPlugin 팩토리)
Channel (channel/)ChannelPluginLoader플러그인 탐색 및 로딩
ChannelPluginRegistry플러그인 등록 및 인스턴스 관리
ChannelMessageRouter메시지 트리거 라우팅
ChannelMarketplaceService플러그인 마켓플레이스
Security (security/)ExecutionFirewall파일 경로 방화벽
GuardianAgentAI 도구 호출 검토
PromptGuardian프롬프트 주입 탐지
RateLimiter속도 제한
InputSanitizer입력 위생 처리
UserPermissionService사용자 권한 관리
ChannelPermissionGateChannel 권한 확인
AuditLogger보안 감사 로그
Media (media/)ImageGenerationService이미지 생성
MusicGenerationService음악 생성
MediaStorageService미디어 파일 저장
MediaResourceService콘텐츠 주소 지정 리소스 저장
MediaConfigService미디어 공급자 설정
AsrService / TtsService음성 인식/합성
Project (project/)ProjectService프로젝트 CRUD
FileIndexService파일 인덱스 서비스
GitServiceGit 작업
Search (search/)WebSearchService통합 검색 진입점
JinaProvider / TavilyProvider / SearxngProvider검색 엔진 어댑터
UI (ui/)ThemeService테마 관리
WindowControlsService창 컨트롤
TrayService시스템 트레이
NotificationService데스크톱 알림
Config (config/)ConfigStoreelectron-store + fs.watch 핫 리로드
Cron (cron/)CronServiceCron 예약 작업 디스패치
MCP (mcp/)McpServiceMCP 서버 관리
Plugins (plugin/)ScriptPluginLoader스크립트 플러그인 로딩
ScriptPluginRegistry스크립트 플러그인 레지스트리
ScriptPluginBridgeManager플러그인 브릿지 관리

IPC 라우터 아키텍처

프론트엔드와 백엔드 간의 모든 통신은 IPC 라우터 레이어를 통해 이루어지며, 현재 68개 이상의 라우터 모듈이 등록되어 있습니다.

secureHandle 패턴

각 IPC 채널은 secureHandle로 감싸져 인증 토큰 유효성 검사를 강제합니다:

{/* packages/desktop/app/main/ipc/safe-handle.ts */}
function secureHandle(
channel: string,
handler: (event: IpcMainInvokeEvent, params: unknown) => Promise<unknown>,
validateToken: (token: string) => boolean,
): void;

흐름:

sequenceDiagram
participant R as Renderer
participant P as Preload
participant S as secureHandle
participant H as Router Handler

R->>P: window.api.someMethod(params)
P->>S: ipcRenderer.invoke(channel, {token, ...params})
S->>S: validateToken(token)
alt token invalid
S-->>P: throw AuthError
else token valid
S->>H: handler(event, params)
H-->>S: result
S-->>P: result
P-->>R: result
end

라우트 등록

모든 라우트는 registerAllRouters()에서 중앙 등록됩니다. 각 Router 클래스는 BaseRouter를 확장하고 register() 메서드에서 채널을 등록합니다:

{/* 단순화된 Router 구조 */}
class SomeRouter extends BaseRouter {
register() {
secureHandle('domain:action', async (_event, params) => {
const validated = SomeSchema.parse(params);
return this.service.doSomething(validated);
}, this.validate);
}
}

라우트 범주 및 수량

라우터 디렉토리/파일채널 접두사주요 채널 수
auth/auth:*5개 이상
account/accounts:*, accountTokens:*8개 이상
session/sessions:*, sessionOrganizer:*10개 이상
chat/chatMessages:*, chatAssistants:*, chatControl:*, mediaSession:*15개 이상
completion/completion:*5개 이상
capabilities/llm/llmProviders:*, llmModels:*, apiKeys:*, transformers:*15개 이상
project/projects:*, files:*, git:*10개 이상
media/media:*, imageProviders:*, musicProviders:*, videoProviders:*, asr:*, tts:*20개 이상
settings/settings:*, appPreferences:*8개 이상
ui/theme:*, window:*6개 이상
MagiRoutermagi:*10개 이상
ChannelPluginRouterchannels:*8개 이상
sillytavern/characterCards:*, worldInfo:*, regexScripts:*, groupChat:*25개 이상
기타mcp:*, webSearch:*, todo:*, tasks:*, notes:*, tags:*, cron:*, elfi:*30개 이상

Worker 스레드 패턴

시간이 많이 걸리는 I/O 작업은 메인 스레드 차단을 방지하기 위해 Worker 스레드에서 실행됩니다.

DbClient — 데이터베이스 Worker

DbClient는 가장 중요한 Worker로, SQLite 데이터베이스와의 모든 상호작용을 캡슐화합니다:

sequenceDiagram
participant S as Service
participant C as DbClient
participant W as db.worker.ts
participant D as SQLite (WAL)

S->>C: db.someMethod(params)
C->>C: seq++, create Promise
C->>W: postMessage({id: seq, method, params})
W->>D: Execute SQL query
D-->>W: Result
W-->>C: postMessage({id: seq, result})
C->>C: resolve(pending[seq])
C-->>S: Promise<result>
{/* 단순화된 DbClient RPC 패턴 */}
class DbClient extends EventEmitter implements DbRpc {
private worker: Worker;
private seq = 0;
private pending = Map<number, {resolve, reject}>;

async send(method: string, params: unknown): Promise<unknown> {
const id = ++this.seq;
return new Promise((resolve, reject) => {
this.pending.set(id, { resolve, reject });
this.worker.postMessage({ id, method, params });
});
}

async ready(): Promise<void>;
}

주요 설계 포인트:

  • 비동기 RPC: 각 호출에 고유한 시퀀스 번호가 부여되며, 요청은 postMessage를 통해 전송됨
  • Ready 대기: await db.ready()는 사용 전에 Worker가 초기화(스키마 생성)를 완료했는지 보장함
  • 오류 전달: Worker 오류는 EventEmitter를 통해 상위로 전파됨

Worker 스레드 목록

Worker 파일목적통신 방식
db.worker.tsSQLite 데이터베이스 읽기/쓰기 (100개 이상의 RPC 메서드)DbClient RPC
fileSearch.worker.ts파일 검색 (퍼지 매칭)MessagePort
fileWatcher.worker.ts파일 시스템 감시 (chokidar)MessagePort
mcp.worker.tsMCP 서버 프로세스 관리MessagePort
diagnostics.worker.ts시스템 진단 데이터 수집MessagePort
project.worker.ts프로젝트 파일 인덱스 빌드MessagePort
fileSnapshot.worker.ts파일 스냅샷 차분MessagePort

핵심 인프라

LoggerService

Winston 기반으로, 로그 레벨 필터링 및 자동 로그 파일 로테이션을 지원합니다:

{/* 단순화된 LoggerService 인터페이스 */}
class LoggerService {
info(message: string, meta?: Record<string, unknown>): void;
warn(message: string, err?: Error, meta?: Record<string, unknown>): void;
error(message: string, err?: unknown, meta?: Record<string, unknown>): void;
debug(message: string, meta?: Record<string, unknown>): void;
}
  • 로그 위치: {userData}/logs/
  • 로테이션 정책: daily-rotate-file, 14일 보관
  • 형식: JSON + 타임스탬프

SecurityService / CryptoService

{/* 핵심 암호화 서비스 인터페이스 */}
class SecurityService {
encrypt(plaintext: string): string;
decrypt(ciphertext: string): string;
}

class CryptoService {
deriveKey(password: string, salt: Buffer): Buffer;
encrypt(data: string, key: Buffer): EncryptedData;
decrypt(data: EncryptedData, key: Buffer): string;
}

AppPaths

모든 애플리케이션 디렉토리 경로를 중앙에서 관리합니다:

프로퍼티경로목적
userData{appData}/elftia/애플리케이션 데이터 루트
dbPath{userData}/elftia.dbSQLite 데이터베이스
configDir{userData}/config/설정 파일 디렉토리
resourcesDir{userData}/resources/미디어 리소스
diagnosticsDir{userData}/diagnostics/진단 데이터
logsDir{userData}/logs/로그 파일

관련 파일

파일설명
packages/desktop/app/main/index.ts메인 프로세스 진입점; 모든 서비스 생성 및 시작
packages/desktop/app/main/services/routers/index.ts라우트 등록 센터, registerAllRouters()
packages/desktop/app/main/services/routers/BaseRouter.tsRouter 기반 클래스
packages/desktop/app/main/ipc/safe-handle.tssecureHandle IPC 보안 래퍼
packages/desktop/app/main/workers/DbClient.ts데이터베이스 Worker 클라이언트
packages/desktop/app/main/workers/db.worker.ts데이터베이스 Worker 구현
packages/desktop/app/main/workers/types.tsWorker RPC 타입 정의
packages/desktop/app/main/services/infra/logger/LoggerService.ts로깅 서비스
packages/desktop/app/main/services/platform/security/SecurityService.ts암호화 서비스
packages/desktop/app/main/services/infra/paths/paths.ts경로 관리
packages/desktop/app/main/services/agent-core/engine/EngineDispatcher.ts엔진 디스패처