Skip to content

moooon

Motir

Vibe your whole project. Bring an idea — Motir's three AI layers plan it, track it, and ship it, end to end. You're looking at Motir, built in Motir.

  • Vibe Project
  • Open Source
  • AI Agent
  • AI Loop
1
requests
0
upvotes
145
planned
1,361
shipped

Motir · Roadmap

MOTIR-4220Implemented

(motir-marketing) Host routing — a request on a tenant or customer host resolves through the public contract and renders that project's /p/* pages at the root

Opened by Zhu Yue ·

motir.co's renderer learns to answer for OTHER hosts. Today motir-marketing has no host awareness at all — no proxy.ts, no middleware, one SITE_ORIGIN baked at build time, and a /p/[identifier] route tree that assumes it is reached by that path on that host (measured on origin/main, 2026-09-02). This card adds the router that turns a request on a tenant or customer host into a render of the right project's existing pages, at the root of that host, without duplicating a single page.

What ships

  • proxy.ts (Next 16's middleware file — the name motir-core uses): on every request, read host (and x-forwarded-host, which Fly sets — prefer the forwarded value and normalise to lowercase without a port). If the host is motir.co / the configured SITE_ORIGIN host or localhost, do nothing — today's behaviour is untouched. Otherwise resolve it through the host contract (GET ${APP_ORIGIN}/api/public/hosts/{host}, honouring its Cache-Control with an in-process cache) and:
    • workspace_subdomainrewrite / to the workspace's public-project list, and /<identifier>[/tab…] to /p/<identifier>[/tab…], when <identifier> is one of the resolution's projects; otherwise notFound;
    • workspace_subdomain_alias301 to the same path on redirectTo;
    • custom_domain (issued) → rewrite /[tab…] to /p/<identifier>[/tab…] — the project lives at the host's root (ADR Q3);
    • 404 from the contract → the site's not-found page (which MOTIR-4193 is giving a chrome); failed (the API unreachable) → the design's ERROR state, never a 404 (lib/publicProject.ts's three-outcome rule).
    • The rewrite carries the resolved host and kind to the page in request headers (x-motir-public-host, x-motir-address-kind) so the pages can build host-relative links and the canonical card can decide redirects.
  • The workspace root page — a small new route the rewrite targets (app/w/[hostname]/page.tsx or the shape the design chose), listing that workspace's public projects as /explore-style cards, built to design/public-projects/ for chrome — no new design asset is needed for a list of the shipped project cards; if the design card's notes say otherwise, stop and read them.
  • Host-relative links: projectTabHref and every href the /p/* pages emit today are site-relative (/p/<identifier>/board); on a tenant host they must become /<identifier>/board (subdomain) or /board (custom domain). Add ONE helper (lib/publicHost.tspublicPathFor(host, identifier, segment)) reading the request header, and route every link through it; a grep for '/p/' string literals in app/p/** after this card finds only the helper.
  • Configuration: NEXT_PUBLIC_MOTIR_TENANT_DOMAIN (the base domain) as a build arg in fly.toml [build.args], the Dockerfile ARG, ci.yml's env and .env.example, exactly as NEXT_PUBLIC_MOTIR_APP_ORIGIN travels — with a lib/tenantDomain.ts accessor that throws a named error when unset in production and defaults for tests. (Set the value in fly.toml on this PR; the provisioning card covers the motir-core side.)
  • Tests (jsdom / node): the router's four arms over a stubbed contract; host normalisation (Host with port, uppercase, x-forwarded-host precedence); the link helper for all three host kinds; the cache honours max-age; the E2E lane can reach a tenant host — Chromium resolves *.localhost to loopback, so acme.localhost:<port> is the harness's tenant host and needs no /etc/hosts change; record this in e2e/stub/origin.ts for the E2E card.

Boundary

No canonical, og:url, sitemap or robots change and no motir.co/p/* redirect — the next card. No page is duplicated: the existing app/p/[identifier]/** tree renders every host. No database client (the standing rule). The certificate is Fly's; this card never sees TLS.

Acceptance criteria

  • With the contract stubbed, a request to acme.<base>/PROD/board renders the same page as motir.co/p/PROD/board; acme.<base>/ renders the workspace's public-project list; roadmap.acme.com/board renders /p/PROD/board for the project the host resolves to; an alias host answers 301 to the live host with the path preserved.
  • A request on motir.co never calls the host contract (asserted with a spy), and a request on an unknown host renders the site's not-found page with a 404 status.
  • Every href emitted on a tenant host is host-relative (no /p/ prefix) — a rendered-page test asserts it for the tab bar, the items pager and a detail link on each host kind.
  • NEXT_PUBLIC_MOTIR_TENANT_DOMAIN is set in fly.toml, the Dockerfile, ci.yml and .env.example, and the build fails with a named error when it is unset in production.
  • pnpm test includes a spec that walks acme.localhost:<port> in the Playwright lane's stub configuration, proving the tenant-host harness is reachable.
  • No @prisma/client or database URL appears in the repository after this card (the standing guard); no file outside motir-marketing is touched.

Context refs

  • motir-marketing/lib/publicProject.ts (PROJECT_TABS, projectTabHref, the three-outcome read) · lib/siteOrigin.ts · lib/appOrigin.ts · app/p/[identifier]/layout.tsx
  • motir-marketing/fly.toml [build.args] · Dockerfile (ARG NEXT_PUBLIC_MOTIR_APP_ORIGIN) · .github/workflows/ci.yml env · .env.example — the build-time variable pattern
  • motir-marketing/e2e/stub/origin.ts · playwright.config.ts — the lane this must stay reachable in
  • motir-core/proxy.ts — the middleware precedent (PUBLIC_REDIRECT_SEGMENTS, the CURRENT_PATH_HEADER warning on trusting request-derived values)
  • the host contract — the DTO union this switches on; the decision — Q2, Q3
  • motir-marketing/design/public-projects/design-notes.md — the chrome and the states

Discussion

No comments yet.

Adding to this discussion signs you in on app.motir.co and brings you back to this request.

Add a comment