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-4114Done

The act path the amendment decided — motir-core's half, with the session cookie left host-only

Opened by Zhu Yue ·

Ship motir-core's half of whatever the amendment decided — the routes, the redirect targets and the posture the act affordances on motir.co call into. This card's scope is the amendment's table, read row by row, restricted to rows whose mechanism needs code in this repository.

What is knowably owed regardless of which mechanism the amendment picks (these hold under every option it can reasonably choose, which is why they are here and not on a follow-up):

  • The overview WRITE has no public door at all. publicProjectsService.setPublicOverview (:444) exists, is tested (tests/publicProjects/publicOverviewEdit.test.ts), and was reached only by the deleted overview-actions.ts Server Action. Whatever the amendment chose — an authenticated route on app.motir.co, or editing that leaves the public page entirely — the mechanism it names is built here.
  • A return-to-the-public-page hand-off. Any row whose mechanism is "go to app.motir.co, act, come back" needs the application to accept a destination and return the visitor to it. proxy.ts already carries the pattern to follow and the warning that goes with it: CURRENT_PATH_HEADER's doc says an unvalidated redirect target from a request is "the one way this small piece of plumbing could ship a vulnerability." A destination arriving from another origin is validated against an allow-list of the public origin — never reflected.
  • Two write routes sit OUTSIDE the public contract and outside the cloud gate: POST /api/public-requests/{id}/upvote and …/comments are under app/api/public-requests/, not app/api/public/, so contract-coverage does not see them, cloud-gate-totality does not gate them, and a self-hosted build answers them. Whatever the amendment made of vote and comment, their placement is disposed of by this card: brought inside the gated, contracted namespace, or left where they are with the reason written in the route's own comment.

The one thing this card may not do, whatever it costs: widen the session cookie. public-surface-hosts.md §4 makes Domain-scoping to .motir.co a rejected alternative, not a deferred one, and the residual exposure the whole arrangement accepts is survivable only on that condition. If the amendment's mechanism appears to require it, the amendment is wrong — stop and say so; do not ship the widening.

Every new or moved route under app/api/public/* owes its contract operation, its cloud gate and its anonymous-posture entry, same as its read siblings.

Acceptance criteria

  • Every row of the amendment's affordance table whose mechanism needs motir-core code is implemented, and the card's own body lists the rows it discharged and the rows it did not (with the reason: no code needed here, or another card owns it).
  • The public overview WRITE reaches setPublicOverview through the mechanism the amendment named, gated so that only a caller the service already authorises as able to manage the project can perform it.
  • Any hand-off destination accepted from outside is validated against the configured public origin and falls back to a fixed safe destination otherwise; a test drives a hostile value (https://evil.example, //evil.example, /../) and asserts the fallback.
  • The placement of POST /api/public-requests/{id}/upvote and …/comments is DECIDED and recorded — either they move under app/api/public/* with contract operations and the cloud gate, or each route's comment states why it stays outside both.
  • lib/auth.ts's session-cookie configuration is unchanged — the diff touches no cookie domain, and the motir-core vitest gate asserts it.
  • Every route this card adds or moves is in lib/api/public/openapi/operations.ts and passes contract-coverage, contract-drift, cloud-gate-totality and anonymous-posture with no new exemption.
  • No file outside motir-core is touched.

Rows discharged — the record this card's first acceptance criterion asks for

⚠️ The amendment landed as AMENDMENT 4, not AMENDMENT 2 (MOTIR-4004 took the ordinal on 2026-09-02; see MOTIR-4108's banner). Its §D table, row by row:

rowmechanismdischarged here?
1 · account menu / sign-in dialogABSENTNo motir-core code needed. It becomes a plain link in motir-marketing's chrome — MOTIR-4113 draws it, MOTIR-4115 renders it.
2 · followHAND-OFFYesapp/act/route.ts.
3 · subscribeANONYMOUS-DIRECTYes — CORS on /api/public/* in proxy.ts, one allow-listed origin, no Allow-Credentials.
4 · roadmap vote / request upvoteHAND-OFFYes — same entry, intent=vote / upvote.
5 · request commentHAND-OFFYes — same entry, intent=comment.
6 · submit a feature requestHAND-OFFYes — same entry, intent=request.
7 · in-place overview editingABSENT from motir.coYesPATCH /api/projects/{key}/public-overview, an APPLICATION route, deliberately outside the public contract. It gives setPublicOverview the caller it has been without since MOTIR-3951.
8 · viewer-awareness on the readsalways anonymousNothing to build. It is a consequence of rows 1–7, not a change: actorUserId is already optional on every read.

Also discharged: app/api/public-requests/{id}/{upvote,comments} gained the MOTIR_CLOUD gate they never carried, and cloud-gate-totality.test.ts now walks that root, so a future route there cannot escape it. They STAY outside app/api/public/*, with the reason in each route's own comment (§F).

Explicitly NOT here: the visual treatment of a hand-off — the control, the interstitial, the return — is MOTIR-4113's (§I), and the public-side controls that call /act are MOTIR-4119's.

Context refs

  • motir-core/docs/decisions/public-surface-hosts.md §4 and its AMENDMENT 4 (⚠️ amended from AMENDMENT 2MOTIR-4004 took that ordinal) — the constraint and the table
  • motir-core/lib/services/publicProjectsService.tssetPublicOverview (:444), getOverview's canManage
  • motir-core/tests/publicProjects/publicOverviewEdit.test.ts — the service test kept for this card
  • motir-core/app/api/public-requests/[id]/upvote/route.ts · comments/route.tsrequireCompliantSession(), outside the contract and the gate
  • motir-core/app/api/public/p/[identifier]/follow/route.ts — the one genuinely session-gated public route
  • motir-core/proxy.tsCURRENT_PATH_HEADER's three properties and the open-redirect warning
  • motir-core/lib/auth.ts — the cookie this card may not widen
  • motir-core/lib/publicProjects/urls.tspublicSiteOrigin(), the configured public origin to validate against
  • the act affordances on motir.co — the consumer of everything here

⚠️ ORDINAL CORRECTED AGAIN, 2026-09-02 — it is AMENDMENT 4. MOTIR-4139 (b615991c4) merged its own AMENDMENT 3 into public-surface-hosts.md — the subprocessor seam — while this story was in flight, so the affordance table is AMENDMENT 4. Every reference to AMENDMENT 2 or AMENDMENT 3 on this card and its siblings means that section. Twice in one day: an ADR ordinal is a serialized resource and nothing in the corpus says so — the amendment's own banner now does.

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