fix(ci): bake api.moleculesai.app into tenant canvas bundle

Canvas's browser-side code (auth.ts, api.ts, billing.ts) all call
fetch(PLATFORM_URL + /cp/*). PLATFORM_URL comes from
NEXT_PUBLIC_PLATFORM_URL at build time; with the build arg unset,
it falls back to http://localhost:8080 in the compiled bundle.

That means on a tenant like hongmingwang.moleculesai.app, the
user's browser actually tried to fetch http://localhost:8080/cp/
auth/me — which resolves to the USER'S OWN machine, not the tenant.
Login redirect loops 404. Every tenant canvas has been unable to
complete a fresh login on this path; existing sessions only worked
because the cookie was already set domain-wide.

Fix: pass NEXT_PUBLIC_PLATFORM_URL=https://api.moleculesai.app
as a build arg in the tenant-image workflow. CP already allows
CORS from *.moleculesai.app + credentials, and the session cookie
is scoped to .moleculesai.app so tenant subdomains inherit it.

Verified in prod by rebuilding canvas locally with the flag and
hot-patching the hongmingwang instance via SSM. Baked chunks now
contain api.moleculesai.app; browser auth redirects resolve
cleanly to the CP.

Self-hosted users override by rebuilding with their own URL —
same pattern molecule-app uses with NEXT_PUBLIC_CP_ORIGIN.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hongming Wang 2026-04-20 12:51:22 -07:00
parent 05aa0cc787
commit ee40880f39

View File

@ -111,6 +111,21 @@ jobs:
${{ env.TENANT_IMAGE_NAME }}:staging-${{ steps.tags.outputs.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
# Bake the SaaS control-plane URL into the canvas bundle.
# Canvas's browser-side code uses PLATFORM_URL for every
# /cp/* call (auth, orgs, billing, terms). Leaving this empty
# made PLATFORM_URL fall back to http://localhost:8080 in the
# built bundle — which fails from the user's browser because
# localhost resolves to their own machine, not the tenant
# instance. Baking the CP origin here fixes browser-side auth
# for every tenant.
#
# Self-hosted / private-label deployments override this by
# rebuilding the image with a different NEXT_PUBLIC_PLATFORM_URL
# build-arg (e.g. https://api.their-domain.com). Same pattern
# molecule-app uses with NEXT_PUBLIC_CP_ORIGIN.
build-args: |
NEXT_PUBLIC_PLATFORM_URL=https://api.moleculesai.app
labels: |
org.opencontainers.image.source=https://github.com/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}