Self host fork prep#1947
Conversation
`multica setup cloud` and `runSetupCloud` hard-coded `https://api.multica.ai` / `https://multica.ai` and were the only mechanism that pointed the CLI at upstream's hosted product. Drop both, fold self-host setup into the bare `multica setup` command, and keep `multica setup self-host` as a backwards-compatible alias. The previous bare `multica setup` ran the cloud configuration; this fork is self-host-only, so the bare command now configures a self-hosted server (defaults to localhost:8080 / localhost:3000 with overrides via --server-url / --app-url). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Previously SendInvitationEmail silently fell back to https://app.multica.ai when FRONTEND_ORIGIN was unset, which on a self-hosted instance produces invite emails whose links go to the upstream hosted product instead of your own server. Replace the fallback with a hard error so misconfigurations surface at the send-attempt rather than after a recipient clicks a dead link. The existing buildInvitationParams unit tests pass inviteURL directly and don't exercise the env-var path, so no test changes are needed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Strip the "Multica Cloud" sales funnels from the user-facing flows since this fork has no cloud product to funnel into. Production code now only ever generates two onboarding completion paths — "full" or "runtime_skipped" — so the cloud-waitlist UI was a dead end. Removed: - packages/views/onboarding/components/cloud-waitlist-expand.tsx and its export from packages/views/onboarding/index.ts. - The "Cloud runtime" ForkAlt card and CloudWaitlistDialog from StepPlatformFork (web Step 3); footer copy + comments updated. - The "Join cloud waitlist" EmptyCard and dialog from StepRuntimeConnect EmptyView (desktop Step 3); waitlist plumbing removed from FancyView and StepRuntimeConnect props. - Shell-level waitlistSubmitted latch + cloud_waitlist branch in OnboardingFlow's completionPath. - apps/web/features/landing/components/download/cloud-section.tsx and its mount on the /download page. - Cloud-waitlist test cases in step-platform-fork.test.tsx; remaining tests now assert the cloud option is absent. Kept (for upstream-merge cleanliness): - packages/core/onboarding/store.ts joinCloudWaitlist(). - packages/core/api/client.ts joinCloudWaitlist API method. - "cloud_waitlist" member of OnboardingCompletionPath union. - DB columns user.cloud_waitlist_email / cloud_waitlist_reason and the /api/me/onboarding/cloud-waitlist server handler. - Unused t.download.cloud locale strings. These are orphaned now but keeping them prevents merge churn whenever upstream touches the API client, types, or schema. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The committed .env.production previously baked api.multica.ai / multica.ai into every desktop build. On a self-hosting fork that's exactly the artifact we never want to ship: a packaged desktop app that silently routes a fork user's traffic to the upstream hosted product. Blank the three VITE_ URLs in .env.production and add a build-time guard in electron.vite.config.ts that throws when any of them is empty during a production build. To package the desktop app, fork maintainers override the values in apps/desktop/.env.production.local (already gitignored by the repo's .env* rule). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Document this fork's invariant ("no code path defaults to *.multica.ai"),
the merge workflow against upstream, the expected conflict zones, and
the desktop-build override recipe. Add a top-of-README banner so the
fork's intent is visible at a glance, and drop the "Switching to Multica
Cloud" section from SELF_HOSTING.md since the CLI subcommand it
described no longer exists.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CLI_INSTALL.md told headless users to grab a PAT from https://app.multica.ai/settings — wrong on a self-host instance. .env.example used multica.ai as the placeholder for ALLOWED_ORIGINS. Replace both with self-host-correct guidance. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Someone is attempting to deploy a commit to the IndexLabs Team on Vercel. A member of the Team first needs to authorize it. |
There was a problem hiding this comment.
Pull request overview
Prepares the repository as a self-host-only fork by removing “Multica Cloud” default behaviors and user-facing cloud waitlist surfaces, and by adding guardrails to prevent builds/emails from silently pointing at upstream hosted domains.
Changes:
- Make self-host configuration the default (
multica setup), keepingsetup self-hostonly as a backwards-compatible alias. - Remove “cloud runtime / waitlist” UI paths from onboarding Step 3 and the landing
/downloadpage. - Add self-host safety guards: require
FRONTEND_ORIGINfor invite emails and fail desktop production builds when requiredVITE_*URLs are unset.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| server/internal/service/email.go | Require FRONTEND_ORIGIN for invitation links; remove hosted fallback. |
| server/cmd/multica/cmd_setup.go | Make setup self-host by default; remove cloud setup path; refactor flag registration. |
| packages/views/onboarding/steps/step-runtime-connect.tsx | Remove cloud waitlist UI from desktop runtime-connect empty state. |
| packages/views/onboarding/steps/step-platform-fork.tsx | Remove cloud runtime waitlist fork from web Step 3. |
| packages/views/onboarding/steps/step-platform-fork.test.tsx | Update Step 3 web tests to match removal of cloud option. |
| packages/views/onboarding/onboarding-flow.tsx | Remove shell-level waitlist tracking; simplify completion-path derivation. |
| packages/views/onboarding/index.ts | Stop exporting removed waitlist component. |
| packages/views/onboarding/components/cloud-waitlist-expand.tsx | Delete shared cloud waitlist form component. |
| apps/web/features/landing/components/download/cloud-section.tsx | Delete download-page cloud waitlist section. |
| apps/web/app/(landing)/download/download-client.tsx | Remove cloud section from download page composition. |
| apps/web/app/custom.css | Update landing-page token override comment after cloud removal. |
| apps/desktop/electron.vite.config.ts | Add production-build guard requiring VITE_API_URL/VITE_WS_URL/VITE_APP_URL. |
| apps/desktop/.env.production | Blank default production URLs and document .env.production.local override. |
| SELF_HOSTING.md | Remove “Switching to Multica Cloud” section. |
| README.md | Add self-host fork banner; remove Cloud link from header. |
| FORK.md | Document fork invariants, intentional diffs, and merge workflow. |
| CLI_INSTALL.md | Replace hosted PAT URL with <your-frontend-origin> placeholder. |
| .env.example | Update allowed-origins example away from multica.ai domains. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if appURL == "" { | ||
| appURL = "https://app.multica.ai" | ||
| return fmt.Errorf("cannot send invitation email: FRONTEND_ORIGIN is unset, so invite links would not resolve to your self-hosted instance") | ||
| } |
There was a problem hiding this comment.
SendInvitationEmail uses FRONTEND_ORIGIN verbatim when building inviteURL. If the env var is configured with a trailing slash (common in ops configs), this will generate URLs like https://example.com//invite/<id>, which can break routing depending on the frontend/server. Consider normalizing with strings.TrimRight(appURL, "/") (or url.JoinPath) before appending /invite/... (note resolveAppURL already trims trailing slashes).
| } | |
| } | |
| appURL = strings.TrimRight(appURL, "/") |
| appURL := strings.TrimSpace(os.Getenv("FRONTEND_ORIGIN")) | ||
| if appURL == "" { | ||
| appURL = "https://app.multica.ai" | ||
| return fmt.Errorf("cannot send invitation email: FRONTEND_ORIGIN is unset, so invite links would not resolve to your self-hosted instance") | ||
| } |
There was a problem hiding this comment.
This change introduces a new hard failure when FRONTEND_ORIGIN is unset. server/internal/service/email.go already has unit tests in email_test.go for invitation email construction; please add a focused test that asserts SendInvitationEmail returns an error when FRONTEND_ORIGIN is empty/unset (and possibly that it succeeds when set). This will prevent regressions where a fallback URL is reintroduced or the error path is accidentally removed.
Single-image dev container that mirrors CI: Ubuntu 22.04 base, Go 1.26.1 (matches server/go.mod), Node 22 + pnpm 10.28.2 (matches package.json packageManager), Docker-outside-of-docker so make dev / make selfhost can spin up sibling containers, plus VS Code Go + ESLint + Tailwind extension hints. postCreateCommand pre-fetches deps so the first interactive session doesn't pay a cold pnpm install / go mod download. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two improvements drawn from a real `make check` run inside the devcontainer: 1. --add-host=host.docker.internal:host-gateway runArg — makes the Docker host gateway resolvable by name from inside the container on Linux too (it's already a default on Docker Desktop). Lets `make check` / `make migrate-up` reach a Postgres running on the host when .env's DATABASE_URL is pointed at host.docker.internal instead of localhost. 2. postCreateCommand also runs `pnpm exec playwright install --with-deps chromium`, so the Playwright phase of `make check` has the headless Chromium binary ready instead of failing with "Executable doesn't exist at /root/.cache/ms-playwright/...". Note: if running `make check` against a host Postgres, swap `@localhost:5432` for `@host.docker.internal:5432` in .env's DATABASE_URL. .env is gitignored, so this stays per-machine. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A self-host fork that ships its own modified CLI/desktop/server binaries
needs to deliver them from its own GitHub releases — not upstream's.
Otherwise the install scripts' curl one-liners would download upstream's
artifacts and clients would run code that still has the cloud subcommand
and the api.multica.ai defaults.
Swap multica-ai/multica → TheophilusChinomona/multica everywhere a
runtime URL or user-facing curl/git-clone instruction lives:
- scripts/install.sh, scripts/install.ps1 (REPO_URL, raw.githubusercontent
bootstraps, release-asset URLs, usage docstrings, printf footers).
- README.md, README.zh-CN.md curl one-liners (CI/stars badges left
pointing at upstream — they reflect upstream's actual CI status).
- CLI_INSTALL.md, CLI_AND_DAEMON.md, SELF_HOSTING.md, SELF_HOSTING_AI.md
(curl + git clone instructions).
- server/internal/cli/update.go GitHub release-API endpoints.
- packages/core/runtimes/queries.ts and
packages/views/runtimes/components/update-section.tsx update checker.
- apps/desktop/src/main/cli-bootstrap.ts (desktop's bundled-CLI download
fallback) and apps/desktop/src/renderer/.../daemon-settings-tab.tsx
help link.
- apps/desktop/package.json repository URL.
- apps/web landing: download-client.tsx, layout.tsx schema sameAs,
github-release.ts release fetcher, shared.tsx githubUrl const,
download/cli-section.tsx curl one-liner.
- packages/views/onboarding/steps/cli-install-instructions.tsx (the
CLI install dialog the onboarding flow surfaces) and
packages/views/onboarding/utils/starter-content-templates.ts.
Intentionally NOT swapped:
- Go module paths github.com/multica-ai/multica/... — that's the rebrand
effort, separate per FORK.md.
- README CI / stars badges — they show upstream's CI/stars; the fork has
no CI yet, so swapping would 404.
- Homebrew tap brew install multica-ai/tap/multica — needs its own fork
tap before swapping; revisit when one exists.
- ghcr.io/multica-ai/multica-{backend,web} image refs in
docker-compose.selfhost.yml / Makefile / .env.example — already
env-overridable; revisit when the fork has its own ghcr namespace.
- Doc links to upstream issues / design context — soft-fork attribution.
Also adds .pnpm-store/ to .gitignore (the devcontainer's bind-mounted
pnpm store ends up there when pnpm install runs inside the container).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… fork The README still pitched Multica Cloud as the default (`multica setup` described as "Connect to Multica Cloud"; a "Self-hosting?" callout implied self-host was the alt path). For this fork, self-host IS the path — there is no cloud product on this fork's domain. Changes: - Quick Install: replace Homebrew + Cloud-default flow with a two-step recipe (build the server with `make selfhost-build`, then install the CLI on developer machines via the install scripts that now point at this fork). Drop the brew install line entirely — the fork has no Homebrew tap, so `brew install multica-ai/tap/multica` would install upstream's CLI which still has the cloud subcommand. - Drop the "Multica vs Paperclip" marketing comparison — its "Cloud-first" deployment row contradicts the fork's identity. - CLI command table: `multica setup` description now says "self-hosted server" (matches the actual behavior after feat(cli): remove Multica Cloud setup path); `multica setup self-host` is documented as a backwards-compatibility alias. - Feature bullet for Unified Runtimes drops the "cloud runtimes" reference since this fork ships no cloud runtime path. - Runtime tooltip: "or a cloud instance" → "CI / build host running the daemon headlessly", which is the actual second use case. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The release workflow was already mostly portable — Docker images use
\${{ github.repository_owner }}/multica-{backend,web} so they auto-
publish to ghcr.io/TheophilusChinomona/* on the fork — but two
release-side configs hardcoded the upstream org and would break the
release job:
- .goreleaser.yml `brews:` block targeted multica-ai/homebrew-tap, which
the fork has no write access to. Drop the entire brews block (with a
comment explaining how to re-enable once a fork tap exists). Stable
versioned + legacy tar.gz/zip archives still get built and uploaded
to GitHub Releases via the `archives:` block.
- apps/desktop/electron-builder.yml `publish.owner: multica-ai` →
TheophilusChinomona, so the desktop release job's
electron-builder publish flow uploads installers to the fork's
GitHub Releases instead of trying (and failing) to publish to
upstream.
The CI / desktop-smoke workflows are unchanged — they're owner-agnostic
and will run as-is on the fork once Actions is enabled.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Now that GitHub Actions is wired up on TheophilusChinomona/multica (prior commit `ci: fork-ize release config`), swap the README badges from multica-ai/multica to the fork's URLs so they reflect the fork's own CI state and stargazers — not upstream's. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…only fork Agent-Logs-Url: https://github.com/TheophilusChinomona/multica/sessions/313402ef-77f2-4cf1-825a-20843235a9e4 Co-authored-by: TheophilusChinomona <109826598+TheophilusChinomona@users.noreply.github.com>
What does this PR do?
Related Issue
Closes #
Type of Change
Changes Made
How to Test
Checklist
AI Disclosure
AI tool used:
Prompt / approach:
Screenshots (optional)