6.5 KiB

Multica

Open source project management with coding agents. Runs the official ghcr.io/multica-ai/multica-backend and multica-web images, plus Postgres (pgvector). Agents run on this host through the multica CLI daemon.

Layout

Service Networks Exposed
postgres internal —
backend internal, traefik traefik: /ws on FRONTEND_HOST; host: 127.0.0.1:8090 (daemon)
frontend internal, traefik traefik: everything else on FRONTEND_HOST
  • internal is a plain bridge for service-to-service traffic and outbound egress (email).
  • Single domain. Next.js proxies /api, /auth, /uploads, /v1 to the backend. It does not proxy websockets, so traefik sends /ws to the backend directly.
  • No separate API domain. That layout requires COOKIE_DOMAIN, otherwise every write fails CSRF (403).
  • The backend entrypoint runs DB migrations on every start.

Setup

  1. cp .env.example .env and fill it in.
    • JWT_SECRET: openssl rand -hex 32
    • POSTGRES_PASSWORD: openssl rand -hex 24. Must be URL-safe; base64 (/, +) breaks DATABASE_URL with "invalid port".
    • MULTICA_VCS_SECRET_KEY: openssl rand -base64 32
  2. Point DNS for FRONTEND_HOST at traefik.
  3. docker compose up -d
  4. Sign in with email. The code is sent via Resend/SMTP, or printed in docker logs multica-backend-1 if email isn't working.
  5. After creating your workspace, optionally set DISABLE_WORKSPACE_CREATION=true and/or ALLOW_SIGNUP=false, then docker compose up -d.

Versioning

Server images and the CLI are released together and must run the same version. The tag is pinned in docker-compose.yml (currently v0.6.5), not in .env. Do not use :latest: a docker compose pull would move the server without the CLI.

Local agent daemon

Runs as the multica service user (system UID, home /var/lib/multica, no password, not in docker) under its systemd user unit. Connects to the backend on 127.0.0.1:${BACKEND_HOST_PORT} (8090; 8080 belongs to traefik). The public domain does not resolve back to this host (hairpin NAT returns the router's self-signed cert), so use localhost.

The home directory is required: it holds ~/.multica (config, token, logs), ~/.claude (Claude Code login), ~/.config/systemd/user (autostart unit), ~/multica_workspaces (task checkouts) and ~/.local/bin (CLIs).

Create the user:

sudo useradd --system --user-group --create-home --home-dir /var/lib/multica --shell /bin/bash multica
sudo loginctl enable-linger multica   # user systemd manager at boot, no login needed

Shell as multica with a systemd user session (used by every command below and in Upgrade):

sudo -iu multica env XDG_RUNTIME_DIR=/run/user/$(id -u multica) \
  DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u multica)/bus bash -l

Inside that shell:

export PATH="$HOME/.local/bin:$PATH"

# Claude Code (per-user install and login)
curl -fsSL https://claude.ai/install.sh | bash
claude                                # log in, then /exit

# multica CLI at the version pinned in docker-compose.yml
VER=0.6.5
cd "$(mktemp -d)"
curl -fsSLO "https://github.com/multica-ai/multica/releases/download/v${VER}/multica-cli-${VER}-linux-amd64.tar.gz"
curl -fsSLO "https://github.com/multica-ai/multica/releases/download/v${VER}/checksums.txt"
sha256sum --check --ignore-missing checksums.txt
tar -xzf "multica-cli-${VER}-linux-amd64.tar.gz" multica
install -m 755 multica ~/.local/bin/multica
cd ~

multica setup self-host --server-url http://localhost:8090 --app-url https://multica.whitney.rip
multica login --token=                # personal access token from Settings → Tokens in the web UI
multica daemon start
multica daemon status                 # expect: running, agent "claude", 1+ workspace
multica daemon autostart enable       # captures current PATH; ~/.local/bin must be on it
  • Detects agent CLIs on PATH (claude here).
  • The autostart unit only inherits PATH. Daemon settings go in multica config set. Other env vars agents need (e.g. GH_TOKEN) go in /var/lib/multica/.config/environment.d/*.conf.
  • For agents that push code, give multica its own git identity and SSH key or token.
  • Status from jr: sudo -u multica XDG_RUNTIME_DIR=/run/user/$(id -u multica) systemctl --user status multica-daemon. Logs: multica daemon logs as multica.
  • Remove the user: sudo loginctl disable-linger multica && sudo pkill -u multica && sudo userdel --remove multica.
  • Sign out of the CLI: multica auth logout. This only deletes the local token; revoke it in the web UI too.

Upgrade

  1. Change both image tags in docker-compose.yml.
  2. docker compose pull && docker compose up -d && docker logs -f multica-backend-1
  3. As multica, reinstall the CLI with the same VER, then multica daemon stop && multica daemon start.
  4. Check that multica version matches the image tag.

Risks

  • Agent access: agents run as multica, which has no docker or sudo, but can still read anything world-readable on this host and reach localhost services (including the backend on 8090).
  • RESEND_API_KEY: the current value doesn't look like a Resend key (those start with re_). Until it's fixed, login codes only appear in the backend logs.
  • No Redis: auth endpoints have no rate limiting (logged at startup). Acceptable while signup is limited. Adding Redis also requires trusted-proxy settings (RATE_LIMIT_TRUSTED_PROXIES, MULTICA_TRUSTED_PROXIES) covering traefik and the frontend container, or all users share one rate-limit bucket.
  • Uploads on local disk (UPLOADS_DATA_DIR): back it up along with POSTGRES_DATA_DIR.
  • Backend on the traefik network (needed for /ws): other containers on that network can reach it directly.

Possible extensions

  • Email via SMTP instead of Resend: set the SMTP_* vars.
  • Google login: GOOGLE_CLIENT_ID/SECRET. The redirect URI is ${FRONTEND_ORIGIN}/auth/callback.
  • GitHub App (PR cards, repo picker): GITHUB_APP_SLUG, GITHUB_WEBHOOK_SECRET, GITHUB_APP_ID, GITHUB_APP_PRIVATE_KEY. Add them to compose. Needs MULTICA_PUBLIC_URL reachable from GitHub.
  • Gitea/Forgejo integration: already enabled (MULTICA_VCS_*). Connect it per workspace in Settings → Integrations.
  • S3 storage, Slack/Lark/Telegram bots, server-side LLM (chat titles), plugins: env vars are documented in upstream .env.example and docker-compose.selfhost.yml. Add the ones you need to the backend environment:.
  • More agents: install another supported CLI (codex, opencode, ...) on the host and restart the daemon.