MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-3876

motir.co answers for the public reading surface — /explore, /docs and the crawl directives move to the brand host

Blocked
Description

motir.co becomes the address of Motir's public reading surface, and motir-core is what answers for /explore, /docs and the crawl directives there. app.motir.co keeps the authenticated application and redirects the paths it is losing.

⚠️ RE-PLANNED 2026-08-29 — this story used to point the other way

It was authored as "the landing comes home": port motir-marketing's landing into motir-core so one origin serves everything. That is rejectedmotir-core is GPL-3.0 and may not carry Motir's marketing material. The landing stays where it is; what moves is the ADDRESS of the surfaces motir-core already owns. Three children are superseded by the reversal and archived with this re-plan; the rest survive and are re-scoped by this story's own planning pass.

What this story delivers

  • The ADRMOTIR-3879, and it is the first card. motir.co must now be composed from TWO origins (motir-marketing for the landing and /legal, motir-core for /explore, /docs and /p/*), and two Fly applications cannot both answer an apex. The record chooses the arrangement, states what the session cookie does under it, and says what a self-hosted build serves at each path. Its inputs are on the card.
  • One robots.txt and one sitemap.xml per host, host-aware — including creating app/robots.ts, which does not exist at all today (MOTIR-3726).
  • The public-site origin split out of MOTIR_BASE_URL, so one variable stops answering two different questions.
  • A single public chrome — header, nav and footer — drawn once and used by every surface on the brand host, including the ones motir-marketing serves. It is one chrome across two repositories, which is what makes it a design card rather than a component.
  • The 301s off app.motir.co, plus the in-code sweep of every reader of the old address.
  • Both story-level test gates.

⚠️ The deploy of this story must change NOTHING a visitor can see

The apex does not move until the cutover story runs, and the gap between this story's deploy and that cutover is unbounded. So everything here ships inert: the public-site origin falls back to the APPLICATION origin while it is unconfigured — not to the dev origin, which would publish localhost canonicals in production for the whole window — and every 301 is a no-op while the two origins are equal, which is otherwise a self-redirect loop on /explore and /docs the moment this deploys. Both are acceptance criteria on the cards that own the switch, tested as their own arms.

Boundary — where this story ENDS

  • It does not move /p/*. Public project pages are MOTIR-3877's, which is blocked_by this story.
  • It does not gate anything. Whether a self-hosted build serves /explore at all is the cloud-gate story's; this one is about the address.
  • It does not move /legal — that content leaves motir-core entirely in its own story.
  • It does not touch the landing, which stays in motir-marketing.
  • It does not throw the switch. The apex still points at motir-marketing when this story is done; the cutover story does that, and is blocked_by this one.
  • It does not move the authenticated application. app/(authed)/ is unchanged and app.motir.co remains the session's host.

Verification recipe

  1. Watch the acceptance video the E2E card publishes: /explore and /docs rendering under the shared public chrome with both hosts simulated, robots.txt and sitemap.xml per host, the moved application paths redirecting, and /p/* still answering 200 on the application host.
  2. Read the PR body's full grep for the old address, with a disposition per hit.
  3. After the deploy, confirm nothing moved yet: app.motir.co/explore still returns 200, and motir.co is still served by motir-marketing.

The live walk on motir.co belongs to the cutover story's recipe, not this one's.

Acceptance criteria

  • Every child is done; the repository set is motir-core alone — one code pull request plus the design child's own approval pull request.
  • The ADR exists, names the apex arrangement, states the session cookie's scope under it, and says what a self-hosted build serves at each public path.
  • There is exactly one robots.txt and one sitemap.xml per host, each listing only what that host serves.
  • Exactly one module reads each origin variable, asserted by a test in the shape tests/hosting/appUrlSeam.test.ts already uses.
  • Deploying this story changes nothing a visitor can see — the unconfigured public-site origin resolves to the application origin, and every redirect is a no-op while the two are equal. Asserted by tests, not by intent.
  • The whole journey holds in the E2E lane with both hosts simulated, evidenced by the acceptance video published to this story.
  • ≥90% coverage on every file the story's PRs touch.

Context refs

  • motir-coreapp/(public)/, lib/baseUrl.ts, lib/publicProjects/urls.ts, proxy.ts, tests/navigation/proxy-matcher.test.ts, tests/hosting/appUrlSeam.test.ts
  • motir-marketing — the landing that STAYS, and the chrome this story's design must match
  • motir-core/docs/decisions/marketing-site-hosting.md — the record the ADR amends; it decided WHERE the site runs and never asked which repository owns what
  • MOTIR-3879 — the ADR, carrying the decided inputs in a comment
  • MOTIR-3726 — a child of this story; creates app/robots.ts