Skip to content

Self host fork prep#1947

Open
TheophilusChinomona wants to merge 13 commits intomultica-ai:mainfrom
TheophilusChinomona:self-host-fork-prep
Open

Self host fork prep#1947
TheophilusChinomona wants to merge 13 commits intomultica-ai:mainfrom
TheophilusChinomona:self-host-fork-prep

Conversation

@TheophilusChinomona
Copy link
Copy Markdown

What does this PR do?

Related Issue

Closes #

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Refactor / code improvement (no behavior change)
  • Documentation update
  • Tests (adding or improving test coverage)
  • CI / infrastructure

Changes Made

How to Test

Checklist

  • I have included a thinking path that traces from project context to this change
  • I have run tests locally and they pass
  • I have added or updated tests where applicable
  • If this change affects the UI, I have included before/after screenshots
  • I have updated relevant documentation to reflect my changes
  • I have considered and documented any risks above
  • I will address all reviewer comments before requesting merge

AI Disclosure

AI tool used:

Prompt / approach:

Screenshots (optional)

TheophilusChinomona and others added 6 commits April 30, 2026 13:26
`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>
Copilot AI review requested due to automatic review settings April 30, 2026 11:44
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 30, 2026

Someone is attempting to deploy a commit to the IndexLabs Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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), keeping setup self-host only as a backwards-compatible alias.
  • Remove “cloud runtime / waitlist” UI paths from onboarding Step 3 and the landing /download page.
  • Add self-host safety guards: require FRONTEND_ORIGIN for invite emails and fail desktop production builds when required VITE_* 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")
}
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Suggested change
}
}
appURL = strings.TrimRight(appURL, "/")

Copilot uses AI. Check for mistakes.
Comment on lines 76 to 79
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")
}
Copy link

Copilot AI Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
TheophilusChinomona and others added 7 commits April 30, 2026 14:10
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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants