Multica Repository Analysis: Architecture
High-Level Architecture
+------------------------------------------------------------------+
| FRONTEND MONOREPO |
| |
| +-------------------+ +-------------------+ +---------------+ |
| | apps/web/ | | apps/desktop/ | | apps/docs/ | |
| | (Next.js 16) | | (Electron 39) | | (Next.js) | |
| +--------+----------+ +--------+----------+ +---------------+ |
| | | |
| +--------v----------+ +-------v-----------+ |
| | platform/ | | platform/ | Platform bridges |
| | (Next.js APIs) | | (Electron APIs) | (routing, storage)|
| +--------+----------+ +--------+----------+ |
| | | |
| +--------v----------------------v----------+ |
| | packages/views/ | Shared pages & |
| | (zero next/*, zero react-router-dom) | components |
| +--------+-----------------+---------------+ |
| | | |
| +--------v--------+ +-----v---------+ |
| | packages/core/ | | packages/ui/ | |
| | (headless logic)| | (atoms, no biz)| |
| | API, stores, | | shadcn/ui | |
| | queries, WS | | design tokens | |
| +-----------------+ +---------------+ |
+------------------------------------------------------------------+
|
| HTTP/REST + WebSocket
|
+----------v---------------------------------------------------+
| GO BACKEND |
| |
| cmd/server/main.go |
| +----------------------------------------------------------+ |
| | Middleware Chain: | |
| | RequestID -> ClientMetadata -> Logger -> Recoverer | |
| | -> CSP -> CORS -> Auth -> WorkspaceMember | |
| +----------------------------------------------------------+ |
| |
| +---------------+ +----------------+ +-----------------+ |
| | handler/ | | service/ | | realtime/ | |
| | (HTTP/WS API) | | (biz logic) | | (Hub + Redis) | |
| +-------+-------+ +-------+--------+ +--------+--------+ |
| | | | |
| +-------v-------------------v--------------------v--------+ |
| | events/Bus | |
| | (in-process synchronous pub/sub) | |
| +---------------------------------------------------------+ |
| | |
| +-------v-----------+ |
| | pkg/db/generated/ | sqlc-generated type-safe queries |
| +-------+-----------+ |
| | |
+----------v-----------------------------------------------------+
|
+-----v------+ +-------------------+
| PostgreSQL | | Redis |
| (pgvector) | | (optional, |
| | | multi-node) |
+-------------+ +-------------------+
+------------------------------------------------------------------+
| DAEMON |
| cmd/multica/ (CLI + daemon mode) |
| |
| +------------------+ +-----------------+ +-----------------+ |
| | daemon/daemon.go | | daemon/execenv/ | | pkg/agent/ | |
| | (task polling, | | (work dirs, | | (unified agent | |
| | heartbeat, | | config inject, | | interface for | |
| | GC) | | skills) | | 10 providers) | |
| +------------------+ +-----------------+ +-----------------+ |
| |
| Communicates with server via HTTP (daemon token auth) |
+------------------------------------------------------------------+
Core Components
1. Go Server (server/cmd/server/)
Entry point: main.go:21-142
Initialization sequence:
- Logger init, env validation (JWT_SECRET, RESEND_API_KEY warnings)
- PostgreSQL connection pool
- Event bus creation (
events.New()) - Realtime hub creation (
realtime.NewHub()) - Optional Redis relay for multi-node (
realtime.NewRedisRelay) - Event listener registration (subscribers, activity, notifications, autopilots)
- Router creation with full middleware chain
- Background workers: runtime sweeper, autopilot scheduler, DB stats logger
- HTTP server start with graceful shutdown (SIGINT/SIGTERM, 10s timeout)
2. Router (server/cmd/server/router.go:57-425)
Route groups with escalating auth requirements:
| Route Group | Auth Level | Examples |
|---|---|---|
/health* |
None | Liveness/readiness probes |
/auth/* |
None | Login, code verification, OAuth |
/ws |
JWT/PAT in first message | WebSocket upgrade |
/api/daemon/* |
Daemon token | Task claiming, progress, heartbeat |
/api/me, /api/workspaces |
User JWT/PAT | User-scoped operations |
/api/issues, /api/agents, etc. |
User + workspace membership | Workspace-scoped operations |
3. Event Bus (server/internal/events/bus.go:1-89)
In-process synchronous pub/sub with panic recovery:
type Event struct {
Type string // "issue:created", "task:completed", etc.
WorkspaceID string
ActorType string // "member", "agent", "system"
ActorID string
Payload any
TaskID string // scope hints for realtime routing
ChatSessionID string
}Events flow: Handler -> Bus.Publish() -> Listeners -> Hub.Broadcast()
Event types (from protocol/events.go): issue, task, chat, autopilot, inbox, comment, reaction, activity, agent, skill, project, member, workspace.
4. Realtime System (server/internal/realtime/)
Hub (hub.go): Manages WebSocket connections per workspace+user. Supports scoped broadcasts:
ScopeWorkspace-- all workspace membersScopeUser-- specific userScopeTask-- per-task stream (MUL-1138)ScopeChat-- per-chat session
Redis Relay (redis_relay.go): Multi-node fanout via Redis Streams (max 10K entries per stream, 90s heartbeat TTL, 5min consumer sweep). Deduplication per client prevents double delivery.
Single-node mode: Hub is the sole broadcaster. No Redis needed for dev/self-host.
5. Handler Layer (server/internal/handler/)
31 handler files. Constructor at handler.go:51-68:
type Handler struct {
Queries *db.Queries
DB dbExecutor
Hub *realtime.Hub
Bus *events.Bus
TaskService *service.TaskService
AutopilotService *service.AutopilotService
EmailService *service.EmailService
Storage storage.Storage
CFSigner *auth.CloudFrontSigner
Analytics analytics.Client
// ...
}6. Service Layer (server/internal/service/)
Thin business logic layer between handlers and database:
- TaskService: Enqueues tasks for issue assignments and @mentions, validates agent existence
- AutopilotService: Dispatches autopilots (create_issue or run_only mode), creates runs
- EmailService: Sends via Resend API
- CronService: Scheduled task execution
7. Database Layer (server/pkg/db/)
sqlc pattern: Hand-written SQL in queries/ (27 files) -> code-generated Go in generated/. Config at sqlc.yaml:
queries: "pkg/db/queries/"
schema: "migrations/"
gen:
go:
emit_empty_slices: true
emit_json_tags: true8. Daemon (server/internal/daemon/)
The daemon is the bridge between the Multica server and local agent CLIs. It runs on the user's machine.
Lifecycle (daemon.go:92-139):
- Resolve auth (daemon token)
- Probe available agent CLIs on PATH
- Register runtimes with server
- Start workspace sync, heartbeat, and GC loops
- Poll for pending tasks and execute them
Execution environment (execenv/):
- Creates isolated directory per task:
{workspacesRoot}/{workspaceID}/{taskID}/ - Injects provider-specific config:
CLAUDE.md,AGENTS.md, orGEMINI.md - Writes skills to provider-native paths (
.claude/skills/,.codex/skills/, etc.) - Supports repo checkout via
multica repo checkout
Dependency Flow
apps/web/ ---imports---> packages/views/ ---imports---> packages/core/
---imports---> packages/ui/
apps/desktop/ ---imports---> packages/views/ (same)
packages/core/ (same)
packages/ui/ (same)
packages/core/ -- zero react-dom, zero UI libs
packages/ui/ -- zero @multica/core imports
packages/views/ -- zero next/*, zero react-router-dom
Hard boundaries enforced by design:
core/has no DOM dependencies -- it's headless business logicui/has no business logic -- pure visual atomsviews/has no framework dependencies -- usesNavigationAdapterfor routing- Platform-specific code lives only in
apps/*/platform/
Data Flow: Issue Creation
User clicks "Create Issue" in UI
|
v
packages/views/issues/ -> useMutation(createIssue)
|
v
packages/core/issues/mutations.ts -> api.createIssue(params)
|
v
packages/core/api/client.ts -> POST /api/issues
| (X-Workspace-Slug header)
v
server: middleware.Auth -> middleware.RequireWorkspaceMember
|
v
handler/issue.go: CreateIssue()
|-- queries.CreateIssue(ctx, params)
|-- bus.Publish(Event{Type: "issue:created", ...})
v
Event listeners:
|-- subscriber_listener: auto-subscribe creator
|-- activity_listener: create activity log entry
|-- notification_listener: notify relevant users
|-- realtime_listener: hub.BroadcastToWorkspace(ws event)
v
WebSocket -> all connected workspace members
|
v
packages/core/realtime/use-realtime-sync.ts
|-- onIssueCreated: invalidate React Query cache
v
UI re-renders with new issue in list/board
Data Flow: Agent Task Execution
Issue assigned to agent (UI or API)
|
v
handler/issue.go: UpdateIssue() detects assignee change
|-- TaskService.EnqueueTaskForIssue()
| |-- queries.CreateAgentTask(status: "queued")
| |-- bus.Publish(EventTaskDispatch)
v
Daemon polling loop (daemon.go)
|-- GET /api/daemon/runtimes/{id}/tasks/pending
|-- POST /api/daemon/runtimes/{id}/tasks/claim
v
daemon.handleTask():
|-- execenv.Prepare() -- create work directory
|-- execenv.InjectRuntimeConfig() -- write CLAUDE.md/AGENTS.md
|-- execenv.InjectSkills() -- write skills to provider paths
|-- BuildPrompt(task) -- construct initial prompt
|-- agent.Execute(ctx, prompt, opts) -- spawn CLI process
v
Agent CLI runs (e.g., claude with stream-json):
|-- Reads CLAUDE.md for environment context
|-- Runs `multica issue get <id>` to understand task
|-- Does work (code changes, analysis, etc.)
|-- Runs `multica issue comment add` to post results
|-- Runs `multica issue status <id> in_review`
v
Daemon collects results:
|-- POST /api/daemon/tasks/{id}/progress (streaming)
|-- POST /api/daemon/tasks/{id}/messages (per-message)
|-- POST /api/daemon/tasks/{id}/complete
|-- POST /api/daemon/tasks/{id}/usage (token counts)
v
Server updates task record, publishes events
|-- hub.BroadcastToWorkspace(task:completed)
v
UI shows task completion, comment appears on issue
Multi-Tenancy
Every query filters by workspace_id. The RequireWorkspaceMember middleware validates membership before any workspace-scoped handler executes. The X-Workspace-ID or X-Workspace-Slug header routes requests.
Workspace isolation is also enforced at the daemon level: each daemon authenticates with a workspace-scoped token and can only claim tasks for its registered runtimes.