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-4218Implemented

(motir-core) The public surface honours registered addresses — CORS and the hand-off return target accept every registered public address, not only motir.co

Opened by Zhu Yue ·

Two shipped modules assume the public site has exactly ONE origin, and after this story it has many. Widen both to the set of REGISTERED addresses — read from the store, cached — and nothing else.

Measured on origin/main (2026-09-02): lib/publicProjects/cors.tspublicCorsHeaders(origin) returns headers only when origin === publicSiteOrigin(); lib/publicProjects/returnTarget.tsresolvePublicReturnTarget accepts a destination only when parsed.origin === publicSiteOrigin(). Both are correct today and both break the moment a page on acme.<base> pages an items list (a browser fetch to app.motir.co — CORS) or a visitor on roadmap.acme.com presses Follow (the hand-off's return — refused, so they land on the signed-in home instead of the page they left).

What ships

  • lib/publicAddresses/allowedOrigins.tsisRegisteredPublicOrigin(origin): Promise<boolean>: true for publicSiteOrigin(), for https://<live workspace_subdomain>, and for https://<issued custom_domain> — a read through the repository's findByHostname, cached in process for 60 s (a positive AND a negative cache, so a bot spraying Origin values cannot turn the CORS check into a database probe per request). Aliases and non-issued domains are NOT origins that may act.
  • cors.tspublicCorsHeaders becomes async and consults the registry; the null-when-not-allowed contract, Vary: Origin, and never Access-Control-Allow-Credentials are unchanged (AMENDMENT 4 §D: the allow-list is a convenience, not a trust boundary, because no credential can ride the request). Every caller of publicCorsHeaders updated for the async signature — grep them and list them in the PR.
  • returnTarget.tsresolvePublicReturnTarget accepts a destination whose ORIGIN is a registered public origin; the positive-comparison posture is kept (an allow-list compared by URL.origin, never a reflection or a startsWith), and the fallback stays HANDOFF_FALLBACK_PATH.
  • appUrlSeam.test.ts's single-reader rule for MOTIR_PUBLIC_SITE_URL still holds: the registry calls publicSiteOrigin(), it does not read the variable.
  • Tests: table-driven over the origin set — the configured site, a live subdomain, an alias (refused), an unverified and an issued custom domain (refused / allowed), https://motir.co@evil.test/ and the other homographs returnTarget.ts's header lists (all refused); the cache's negative arm (one DB read for N identical unknown origins); a real hand-off through app/act/route.ts returning to a custom-domain page.

Boundary

No route added, no DTO change, no session or cookie attribute touched — sameSite: 'lax' and the host-only cookie are public-surface-hosts.md §4's and stay exactly as they are; a test in this card re-asserts the cookie attributes did not move. Reads the store only (the store).

Acceptance criteria

  • A fetch from a live tenant subdomain origin to a public read route receives Access-Control-Allow-Origin echoing that origin and Vary: Origin, and NO Access-Control-Allow-Credentials; an alias origin and an unverified custom domain receive no CORS headers.
  • app/act/route.ts returns a visitor to a return URL on an issued custom domain, and still falls back to HANDOFF_FALLBACK_PATH for every homograph and for a non-registered origin.
  • N requests with the same unknown Origin within the cache window perform one repository read (asserted with a spy).
  • lib/auth/index.ts's session-cookie attributes are byte-identical to origin/main (a snapshot test), and no Domain attribute is introduced.
  • No file outside motir-core is touched.

Context refs

  • motir-core/lib/publicProjects/cors.ts · returnTarget.ts — the two modules, and their header comments on why the allow-list is positive and credential-free
  • motir-core/app/act/route.ts — the hand-off (MOTIR-4114)
  • motir-core/docs/decisions/public-surface-hosts.md §4 + AMENDMENT 4 §B/§D — the constraints this must not loosen
  • motir-core/tests/hosting/appUrlSeam.test.ts — the single-reader rule
  • the storefindByHostname

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