Project architecture
Learn how Multica's clients, Go service, execution daemon, and shared frontend packages work together.
Multica consists of a Go backend, multiple clients, and a daemon that runs on execution computers. PostgreSQL stores collaboration data; the daemon claims tasks and invokes local AI coding tools.
Web / Desktop / Mobile / CLI
│
HTTP + WebSocket
│
Go API ───────── PostgreSQL
│
daemon WebSocket
│
local daemon ─── AI coding toolsFor a product-facing explanation, see How Multica works. This page focuses on how the code is layered.
Repository layout
| Directory | Responsibility | Main technologies |
|---|---|---|
server/ | API, authentication, task scheduling, integrations, CLI, and daemon | Go, Chi, sqlc, gorilla/websocket |
apps/web/ | Browser client and landing page | Next.js App Router |
apps/desktop/ | Desktop client and local process management | Electron, electron-vite |
apps/mobile/ | Standalone iOS client | Expo, React Native |
apps/docs/ | Multilingual documentation site | Next.js, Fumadocs |
packages/core/ | API client, types, queries, mutations, and platform-independent business logic | TanStack Query, Zustand |
packages/ui/ | Foundational UI with no business logic | shadcn, Base UI |
packages/views/ | Business pages and components shared by Web and Desktop | React |
packages/tsconfig/, packages/eslint-config/ | Shared tooling configuration | TypeScript, ESLint |
Shared packages export .ts and .tsx source files directly and are compiled by their consuming applications. The dependency direction is views → core + ui; core and ui do not depend on each other.
Code sharing between Web and Desktop
Web and Desktop share three layers:
packages/corehandles APIs, caching, permissions, and platform-independent state.packages/uiprovides foundational components.packages/viewscomposes business pages.
Platform capabilities such as routing, cookies, and Electron IPC stay in the application layer. Shared pages navigate through NavigationAdapter and do not import next/* or react-router-dom directly.
For example, an issue feature needed by both Web and Desktop usually touches:
packages/core/issues/ queries, mutations, cache updates
packages/views/issues/ pages and business components
apps/web/platform/ Next.js routing adapter
apps/desktop/.../platform/ Electron routing adapterMobile does not reuse these React pages. It may import types and pure functions from @multica/core, but it owns its UI, query keys, state, realtime subscriptions, and release process.
Frontend state
Server data and client state are managed separately:
- TanStack Query owns server data such as issues, agents, members, and inbox items.
- Zustand owns client state such as filters, drafts, dialogs, and layout.
- The current workspace is route-driven and is mirrored at the platform layer only where requests, persisted namespaces, or reconnection require it.
- React Context carries only platform plumbing such as the workspace ID and navigation adapter.
WebSocket events should update or invalidate the TanStack Query cache. Do not copy server objects into Zustand. Mutations that navigate, such as creating, deleting, or leaving a workspace, must wait for server confirmation before clearing local state.
API responses are parsed with zod schemas at the packages/core/api/ boundary. An installed Desktop client may connect to a newer backend, so network JSON must not be asserted directly as a TypeScript type.
Backend layers
The main entry points are in server/cmd/:
| Entry point | Purpose |
|---|---|
server | Starts the HTTP API, WebSocket services, scheduler, and integration workers |
multica | CLI and local daemon |
migrate | Runs database migrations |
backfill_* | Data backfill tools for specific releases |
Requests usually flow in this direction:
router → middleware → handler → service → sqlc query → PostgreSQLinternal/middleware/handles authentication, workspace, and request boundaries.internal/handler/parses HTTP input and produces responses.internal/service/owns cross-query business workflows and transactions.pkg/db/queries/contains handwritten SQL.pkg/db/generated/is generated by sqlc and must not be edited directly.internal/integrations/handles external events from GitHub, Slack, Feishu, and other services.internal/storage/handles local and S3 attachments.
PostgreSQL is the source of truth for business data. Redis is optional and is used for cross-instance realtime events, caching, or temporary coordination. Without Redis, a single-instance development environment uses in-process implementations.
Realtime connections
Multica has two distinct WebSocket paths:
internal/realtime/pushes issue, comment, inbox, and other changes to user clients.internal/daemonws/connects daemons to wake runtimes and perform daemon RPC.
WebSocket reduces latency, but the database remains the final state. Clients must recalibrate through queries after reconnecting. The daemon also keeps a polling path so that one disconnect cannot leave a queued task stuck forever.
Code path for one execution
- A user assigns an issue, mentions an agent, or an automation triggers.
TaskServicecreates a queued task and notifies the corresponding runtime.- The daemon claims the task through the daemon API.
- The server issues temporary credentials bound to the task and agent.
- The daemon prepares a local directory and invokes the matching provider backend in
pkg/agent. - The tool runs locally while the daemon uploads progress, messages, and final status.
- The server updates the task and issue, then refreshes clients through realtime events.
The provider adapter layer normalizes startup, streaming events, cancellation, and usage data across AI coding tools, while the daemon still owns local directories and sessions.
Multi-workspace boundaries
Business queries must be scoped by workspace_id, and membership is checked before a request enters a workspace route. X-Workspace-ID selects the current workspace but does not replace authorization checks.
An issue's assignee is polymorphic and may point to a member, agent, or squad. New queries, cache keys, and realtime events must preserve both the workspace and assignee type instead of assuming that a resource ID is globally unique context.
Next steps
- Contributing — Local environments, worktrees, and test locations.
- Development conventions — Repository contracts for naming, terminology, and Chinese copy.