Skip to content

feat(canvas): SaaS cross-origin — slug header + cookie credentials (Phase F) - #91

Merged
HongmingWang-Rabbit merged 1 commit into
mainfrom
feat/canvas-saas-cross-origin
Apr 15, 2026
Merged

feat(canvas): SaaS cross-origin — slug header + cookie credentials (Phase F)#91
HongmingWang-Rabbit merged 1 commit into
mainfrom
feat/canvas-saas-cross-origin

Conversation

@HongmingWang-Rabbit

Copy link
Copy Markdown
Contributor

Canvas client-side pair to molecule-controlplane#13. Derives tenant slug from window.location.hostname, sends X-Molecule-Org-Slug + credentials:include on every fetch, session cookie domain handles cross-subdomain cookies.

Test plan

  • 447/447 canvas tests green (+6 new tenant.test.ts)
  • Post-merge + post-deploy: visit https://acme.moleculesai.app, network tab shows cross-origin fetches to app.moleculesai.app with X-Molecule-Org-Slug: acme and session cookie attached.

🤖 Generated with Claude Code
EOF
)

…hase F)

Canvas will be served at <slug>.moleculesai.app (Vercel). API calls go
cross-origin to https://app.moleculesai.app. This commit wires the
client side:

- canvas/src/lib/tenant.ts: getTenantSlug() derives the slug from
  window.location.hostname, case-insensitive, matching the control
  plane's reservedSubdomains list (app/www/api/admin/…). Server-side
  + localhost + vercel preview URLs + apex all return "" so local dev
  keeps working.

- canvas/src/lib/api.ts: adds X-Molecule-Org-Slug header + sets
  credentials:"include" on every fetch. The control plane's CORS
  middleware allows the origin + credentials; the session cookie has
  Domain=.moleculesai.app so the browser ships it.

- canvas/src/lib/api/secrets.ts: same treatment (secrets API uses its
  own fetch helper — shared slug+credentials logic applied).

Tests: +6 (tenant.test.ts covers slug / reserved / case / non-SaaS /
preview URL / apex). Full canvas suite 447/447 green.

Not in this PR:
- WS URL derivation for terminal/socket.ts (separate follow-up; WS
  needs its own slug-aware URL and the canvas terminal isn't used in
  SaaS launch day-one).
- Next.js rewrites (decided against; cross-origin with credentials
  is cleaner than path-level rewrites for session cookies).

Deploys to Vercel once merged — no manual config needed (env already set).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@HongmingWang-Rabbit
HongmingWang-Rabbit merged commit e727553 into main Apr 15, 2026
7 checks passed
@HongmingWang-Rabbit
HongmingWang-Rabbit deleted the feat/canvas-saas-cross-origin branch April 15, 2026 03:10
molecule-ai Bot pushed a commit that referenced this pull request Apr 20, 2026
…rors

billing.ts (startCheckout, openBillingPortal): replace raw res.text()
in thrown Error with a safe status-only message. The response body from
/cp/billing/* routes can contain Stripe API error detail (invalid key,
card decline message, raw Stripe envelope) that should not reach clients.

orgs/page.tsx (createOrg): same fix — raw body → safe message.

Full body is logged server-side for debugging.

Closes: #91 (CWE-209 — Stripe key echoed in error)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
molecule-ai Bot pushed a commit that referenced this pull request Apr 21, 2026
feat(canvas): SaaS cross-origin — slug header + cookie credentials (Phase F)
molecule-ai Bot pushed a commit that referenced this pull request Apr 21, 2026
…rors

billing.ts (startCheckout, openBillingPortal): replace raw res.text()
in thrown Error with a safe status-only message. The response body from
/cp/billing/* routes can contain Stripe API error detail (invalid key,
card decline message, raw Stripe envelope) that should not reach clients.

orgs/page.tsx (createOrg): same fix — raw body → safe message.

Full body is logged server-side for debugging.

Closes: #91 (CWE-209 — Stripe key echoed in error)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.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.

1 participant