[research] Space notice / MOTD banner — pick the spec-compliant data source #111

Closed
opened 2026-09-17 13:27:07 -04:00 by jared · 2 comments
Owner

Question raised 2026-09-17: is a space-wide notice standard, how would it be stored, and what do other clients see?

There is no MOTD/banner event in the Matrix spec. Three compliant ways to carry the data, from most to least interoperable:

  1. Pinned message in a designated announcements room (m.room.pinned_events, spec). Lotus renders the latest pin of the space's announcements room as a dismissible banner across the space's rooms; Element/other clients see it as an ordinary pinned message in that room. Nothing custom on the wire.
  2. Space topic (m.room.topic / MSC3765 rich topics — Lotus already renders rich topics). Shown by every client in the space header; Lotus would additionally surface it in child rooms. Less flexible (one topic, no expiry).
  3. Custom state event io.lotus.notice on the space (spec allows namespaced custom events). Only Lotus renders it; other clients ignore unknown state events silently (Element shows nothing unless "show hidden events" is on). Most flexible (expiry, severity), least interoperable.

Research deliverable

  • Recommend (1) unless a reason emerges; design: which room is "the announcements room" (space child with a tag? first child named #announcements? explicit io.lotus.space_config?), dismissal state (local vs m.read), expiry, PL gating of who can pin there.
  • Mock-up of the banner in the room header area, mobile and desktop.
  • No implementation until reviewed.
Question raised 2026-09-17: is a space-wide notice standard, how would it be stored, and what do other clients see? **There is no MOTD/banner event in the Matrix spec.** Three compliant ways to carry the data, from most to least interoperable: 1. **Pinned message in a designated announcements room** (`m.room.pinned_events`, spec). Lotus renders the latest pin of the space's announcements room as a dismissible banner across the space's rooms; Element/other clients see it as an ordinary pinned message in that room. Nothing custom on the wire. 2. **Space topic** (`m.room.topic` / MSC3765 rich topics — Lotus already renders rich topics). Shown by every client in the space header; Lotus would additionally surface it in child rooms. Less flexible (one topic, no expiry). 3. **Custom state event** `io.lotus.notice` on the space (spec allows namespaced custom events). Only Lotus renders it; other clients ignore unknown state events silently (Element shows nothing unless "show hidden events" is on). Most flexible (expiry, severity), least interoperable. ### Research deliverable - Recommend (1) unless a reason emerges; design: which room is "the announcements room" (space child with a tag? first child named `#announcements`? explicit `io.lotus.space_config`?), dismissal state (local vs `m.read`), expiry, PL gating of who can pin there. - Mock-up of the banner in the room header area, mobile and desktop. - No implementation until reviewed.
jared added this to the Features 2026-Q4 milestone 2026-09-17 13:27:07 -04:00
jared added the enhancementpriority: lowarea: navigationresearch labels 2026-09-17 13:27:07 -04:00
jared self-assigned this 2026-09-17 13:27:07 -04:00
Author
Owner

Recommendation: option 1 (pinned message in an announcements room), with these specifics

Which room is "the announcements room". Explicit, not guessed: a space-level state event io.lotus.space_config (state_key "") with { "announcements_room": "!room:server" }, set from Space Settings by anyone who can send state in the space (the same PL as editing the space name). Reasons over the alternatives: a name match (#announcements) breaks on rename/i18n and multiple candidates; a tag is per-user and doesn't sync to others. The config event is custom, but it only points — the content people see stays a standard pinned message, so Element still shows it in that room's pin list and nothing else on the wire is Lotus-only. When no config is set, no banner; no heuristics.

What's shown. The newest event in m.room.pinned_events of that room (last id in pinned — the spec order is chronological), rendered from its body/formatted body as one line (clamped to two on desktop, one on mobile, "…more" opens the message in the announcements room). Only m.text/m.notice (skip media pins). Sender + relative time in the trailing slot.

Who can post. Whoever can pin in that room (m.room.pinned_events PL, default 50 — the existing Pin permission). No new permission surface.

Dismissal. Local, keyed by event id, in localStorage (lotus-notice-dismissed-<spaceId> = pinned event id) — private state, no m.read side effects (marking the announcements room read would be wrong when you only dismissed a banner). A new pin = new id = the banner is back. Never synced: a notice you read on your phone is still worth a glance on desktop, and it costs one tap.

Expiry. None in v1 beyond unpinning (the pinner's job; m.room.pinned_events has no TTL). If needed later, a io.lotus.expires_at field on the pinned message's content is a compliant, ignorable extension. Not in scope now.

Placement (mock-up, no new surface): a single-line strip in the same slot the clock-skew banner (#158) and the maintenance banner (#124) use — directly under the room header, full width, Info tint, 📌 + text + × — on every room of the space (and the space lobby), not on rooms outside the space. Mobile identical, one line, text truncated with the tap-to-open. It competes with #124 for the slot: priority maintenance > notice, never stacked (same rule as the security banner proposal in #123).

Cost. utils/spaceNotice.ts (resolve config → pinned event → banner model, pure, ~60 lines + tests), a SpaceNoticeBanner (~80 lines) mounted where the skew banner is, and a Space Settings → General "Announcements room" picker (~60 lines). No fork work. Waiting for review before building, per the issue.

## Recommendation: option 1 (pinned message in an announcements room), with these specifics **Which room is "the announcements room".** Explicit, not guessed: a space-level state event `io.lotus.space_config` (state_key `""`) with `{ "announcements_room": "!room:server" }`, set from Space Settings by anyone who can send state in the space (the same PL as editing the space name). Reasons over the alternatives: a name match (`#announcements`) breaks on rename/i18n and multiple candidates; a tag is per-user and doesn't sync to others. The config event is custom, but it only *points* — the content people see stays a standard pinned message, so Element still shows it in that room's pin list and nothing else on the wire is Lotus-only. When no config is set, no banner; no heuristics. **What's shown.** The newest event in `m.room.pinned_events` of that room (last id in `pinned` — the spec order is chronological), rendered from its body/formatted body as one line (clamped to two on desktop, one on mobile, "…more" opens the message in the announcements room). Only `m.text`/`m.notice` (skip media pins). Sender + relative time in the trailing slot. **Who can post.** Whoever can pin in that room (`m.room.pinned_events` PL, default 50 — the existing Pin permission). No new permission surface. **Dismissal.** Local, keyed by event id, in `localStorage` (`lotus-notice-dismissed-<spaceId>` = pinned event id) — private state, no `m.read` side effects (marking the announcements room read would be wrong when you only dismissed a banner). A new pin = new id = the banner is back. Never synced: a notice you read on your phone is still worth a glance on desktop, and it costs one tap. **Expiry.** None in v1 beyond unpinning (the pinner's job; `m.room.pinned_events` has no TTL). If needed later, a `io.lotus.expires_at` field on the pinned message's content is a compliant, ignorable extension. Not in scope now. **Placement (mock-up, no new surface):** a single-line strip in the same slot the clock-skew banner (#158) and the maintenance banner (#124) use — directly under the room header, full width, `Info` tint, 📌 + text + × — on every room of the space (and the space lobby), not on rooms outside the space. Mobile identical, one line, text truncated with the tap-to-open. It competes with #124 for the slot: priority maintenance > notice, never stacked (same rule as the security banner proposal in #123). **Cost.** `utils/spaceNotice.ts` (resolve config → pinned event → banner model, pure, ~60 lines + tests), a `SpaceNoticeBanner` (~80 lines) mounted where the skew banner is, and a Space Settings → General "Announcements room" picker (~60 lines). No fork work. Waiting for review before building, per the issue.
jared added the needs-human-review label 2026-09-20 15:33:10 -04:00
Author
Owner

Owner decision (2026-10-23): not building the space notice banner. Pins and the space topic cover it as they are. Closing.

Owner decision (2026-10-23): **not building** the space notice banner. Pins and the space topic cover it as they are. Closing.
jared closed this issue 2026-10-23 19:52:27 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/cinny#111