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
146
planned
1,367
shipped

Motir · Roadmap

MOTIR-3879Done

Decision (ADR) — one origin per audience: motir.co is served by motir-marketing over motir-core's public API, and motir-core keeps no public pages

Opened by Zhu Yue ·

Type: decision · Executor: coding_agent · Write docs/decisions/public-surface-hosts.md in motir-core, and AMEND docs/decisions/marketing-site-hosting.md to point at it. No application behaviour ships in this subtask.

⚠️ THIRD AND FINAL RE-SCOPE, 2026-08-29 — read this before anything below

This card has been authored against three arrangements in one day. Its original body recommended option A (motir.co served by motir-core, the landing moves in) and told the record to reject B, C and D. A was rejected — motir-core is GPL-3.0 and may not carry Motir's marketing material. A second re-scope pointed it at composing the apex from two origins. Both are superseded. The decided arrangement is:

hostapplicationserves
motir.comotir-marketingthe landing, /legal, /explore, /docs, /p/* — every public page, rendered over motir-core's public read API
app.motir.comotir-coreeverything behind getSession(), plus the anonymous public read API

motir-core ships NO public rendering at all, and public projects are a CLOUD capability: with MOTIR_CLOUD false the feature is absent, not hidden, because self-hosting is a team doing project management for itself — single-tenant.

The per-surface assignment is a GIVEN, not this record's question. Write it down with its reasoning; do not re-litigate it.

What this record DECIDES

  1. The seam. motir-core exposes an anonymous read API; motir-marketing is its consumer. Record what that makes the API — a published contract with a version and a deprecation policy — and why an internal BFF could not stay internal once a second repository renders from it.
  2. The session cookie. Tenant-authored content now lives on motir.co, which is the PARENT domain of app.motir.co. The cookie stays host-only on app.motir.co — say so, and cite GitHub's "Yummy cookies across domains" and the Public Suffix List for why the arrangement is a deviation from every mirror.
  3. ⚠️ THE DEVIATION, STATED RATHER THAN GLOSSED. Notion publishes on <workspace>.notion.site, GitHub on <user>.github.io, Vercel on *.vercel.app, Canny on <company>.canny.io, Statuspage on <company>.statuspage.ioevery mirror puts tenant-authored content on a SEPARATE REGISTRABLE DOMAIN, and all three of the first are on the Public Suffix List so browsers treat them as separate sites. Serving /p/* from motir.co is better than today (it leaves the session's own origin) and weaker than the mirrors. Record the residual exposure — cookie tossing, session fixation, a phishing page borrowing the parent's credibility — and the reversal condition, which is MOTIR-3878's per-tenant addressing: a separate domain there resolves it.
  4. What a SELF-HOSTED build serves at each public path with MOTIR_CLOUD false. /explore and /p/*: the capability is absent. /legal: gone from the repository. /docs: present. A 404 is a decision, not a default — say which it is for each.
  5. Who owns robots.txt and sitemap.xml per host, now that one host serves the public surface and the other serves an application and an API.
  6. The COSTS, written as costs: a network hop where a Prisma read used to be, so empty and error states become real; a cross-repo contract that can break a live site; two implementations of one chrome asset; and /docs generated from motir-core's registries but rendered in another repository, which is a mirror unless it is a published artifact.
  7. The reversal condition, concretely.

Rung-2 evidence (grep origin/main, not a stale checkout)

  • app/api/public/ holds ten routes, and exactly ONE is session-gatedfollow (POST/DELETE, 401), an account relationship. explore and categories make zero getSession() calls; explore/route.ts says so itself: "NOT session-gated: a logged-out visitor / crawler reads it … deliberately no getSession() call." The /p/* reads take an optional actorUserId = session?.user.id ?? null and are "NOT session-gated on READ."
  • There is no app/api/public/p/[identifier]/route.ts — the project's own subject has no endpoint while all four of its lists do. The sibling card adds it.
  • app/(public)/p/ is 12 files, one of them a Server Actions module.
  • ⚠️ Earlier readings of this surface were WRONG IN BOTH DIRECTIONS and the record should say so: a count of "six session-gated" and a later "eight" were both artefacts of grepping for the string getSession and matching COMMENTS, including the one that says the call is deliberately absent. The correction is on MOTIR-3877.
  • motir-core/lib/baseUrl.ts — one variable answering two questions; the sibling card splits them.
  • motir-core/lib/billing/availability.ts + docs/decisions/billing-tiering.md §6 — MOTIR_CLOUD, the existing explicit flag, and its two-questions-two-functions rule.
  • motir-core/lib/apiDocs/{reference,mcp}.ts — what /docs is generated from, and why its move is a decision rather than a port.

Rung-1 evidence (observe, do not recall)

Vercel and Notion each serve marketing plus first-party content from one origin (vercel.com/docs, notion.com/help); Stripe splits reference docs to docs.stripe.com — a hostname per origin. For TENANT content the mirrors are unanimous and are cited in §3.

Acceptance criteria

  • motir-core/docs/decisions/public-surface-hosts.md exists in the directory's Status → Context → Decision → Consequences shape, with a numbered-Q section and a per-Q rejected-alternatives table, matching marketing-site-hosting.md's convention.
  • It carries the per-surface host and repository table above, with the open-core reason per row, and records /p/* as DECIDED — not deferred.
  • §3's deviation is stated on the record, with the mirrors named, the residual exposure described, and the reversal condition pointing at per-tenant addressing.
  • The session-cookie scope, the self-host behaviour per path, and the robots / sitemap ownership are each stated explicitly.
  • The costs in §6 are written as costs, not as a case for the decision.
  • Every rung-2 claim appears with the file it was read from, including the correction to the earlier miscounts; every rung-1 claim names the product and what was observed.
  • marketing-site-hosting.md gains a dated AMENDMENT naming which of its answers stand (Q3 CI, Q4 subprocessor) and which are superseded.
  • Options rejected along the way — the landing moving into motir-core, and composing the apex from two origins — each get a rejected-alternatives row with the reason, so a future reader does not re-propose them.

Context refs

  • motir-core/docs/decisions/{marketing-site-hosting,application-hosting,billing-tiering,platform-staff-auth,public-api-conventions}.md
  • motir-core/app/api/public/ · lib/baseUrl.ts · lib/billing/availability.ts · lib/apiDocs/
  • MOTIR-3877 — the corrected measurement of the public API
  • MOTIR-3908 — the cloud gate this record describes
  • GitHub, Yummy cookies across domains; the Public Suffix List

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