AI-native task management where coding agents are first-class teammates. Deploys PostgreSQL (pgvector), the Go backend, and the Next.js web app.

Three services, no external object storage required. Attachments go to a persistent volume on the backend.
RESEND_API_KEY empty, read the code from the backend
service log — search for Verification code.ALLOW_SIGNUP=false and
DISABLE_WORKSPACE_CREATION=true on the backend, then restart it.The daemon runs on your own computer, not on Zeabur. Multica's server is only the API, WebSocket hub, and database.
brew install multica-ai/tap/multica
multica setup self-host \
--server-url https://YOUR_API_DOMAIN \
--app-url https://YOUR_DOMAIN
The two URLs are different on purpose. --app-url is the web app you log
in to; --server-url is the API domain, which the CLI probes at
/health before it will save anything. That path is not one the web app
forwards, so pointing --server-url at the app domain makes setup report
the server as unreachable.
Browsers are unaffected either way — they stay on the app domain for
everything including the WebSocket, so cookies never cross origins and
COOKIE_DOMAIN stays unset.
The browser side follows whatever host you visit: the web app serves
/api, /auth, /uploads and /ws from its own origin, and the client
derives wss:// from window.location. Nothing there is pinned to a
domain, so the UI and the WebSocket keep working on a new one with no
configuration.
The backend is the part that will not notice. It never reads the request host — every public URL it emits comes from environment variables. After binding a new domain, update these on the backend service and restart it:
| Variable | Set it to | What breaks if it is stale |
|---|---|---|
MULTICA_APP_URL | new app origin | Onboarding shows the old host; invite and login-code emails link to it |
FRONTEND_ORIGIN | new app origin | Fallback for the above; CSRF origin check |
CORS_ALLOWED_ORIGINS | new app origin | Cross-origin calls rejected |
MULTICA_PUBLIC_URL | new API origin | multica setup self-host hands the daemon the old URL |
Scheme included, no trailing slash. Every one of these fails quietly —
the app still loads and looks fine. After changing them, open
/api/config and confirm daemon_app_url and daemon_server_url both
match the domains you are actually on.
A caveat worth knowing before you attach a custom domain:
MULTICA_PUBLIC_URL ships as ${ZEABUR_API_URL}, and that built-in
resolves to the generated *.zeabur.app domain even after you bind a
custom one alongside it. It does not follow the custom domain. So the
moment you put your own domain on the backend, replace
${ZEABUR_API_URL} with that domain, written out in full.
Then re-run multica setup self-host with the new --server-url on every
machine running a daemon — the CLI stores the URL, so existing daemons
keep using the old one until you do.
S3_BUCKET is left unset, so the backend falls back to local disk and
writes to the /app/data/uploads volume. To move to S3-compatible storage
later (R2, MinIO, B2), set S3_BUCKET, AWS_ENDPOINT_URL,
AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY on the backend. Add
ATTACHMENT_DOWNLOAD_MODE=proxy when the endpoint is not reachable from
the browser. Existing files are not migrated automatically.
The backend is pinned to one replica by the uploads volume. Switch to S3
first if you need more, and add REDIS_URL — without Redis, realtime
events fall back to in-process memory and auth rate limiting is off.
Multica is licensed under a modified Apache License 2.0 with additional conditions covering hosted/embedded commercial use, branding, and attribution. Internal use within a single organization does not require a commercial license; offering it as a service to third parties does. Removing the Multica name, logo, or copyright notices from the UI requires a written branding waiver.
Source: https://github.com/multica-ai/multica · License: https://github.com/multica-ai/multica/blob/main/LICENSE