feat: Discord-style auto room access for space members (restricted join_rule) #7

Closed
claude wants to merge 0 commits from feat/restricted-space-rooms into main
Contributor

Discord-style automatic room access for space members, picked from the 2026-09-16 live multi-user testing report ("click a link, you're on the server, and you're automatically in every room you have permission for — even rooms created later").

What this does

  • New rooms created inside a space now get a restricted join_rule with allow: [{type: m.room_membership, room_id: <space>}] (MSC3083): space membership = join permission, automatically, for members who join later and rooms created later. With the space's "allow join requests" toggle ON, the room gets knock_restricted instead (space members still auto-join; outsiders can knock).
  • Space creation asks for this policy ("Allow join requests from non-members", default OFF) with a plain-language hint, per the owner's request.
  • Changeable later in Space Settings (Access section).
  • Existing rooms are never silently rewritten — but the per-room join rule can be retrofitted from room settings.
  • Space hierarchy join flow (SpaceHierarchyModal) unchanged — restricted rooms join directly for space members, no invite needed.

Why restricted and not invite/knock

Plain invite join_rule (the previous default) is why the admin-added tester saw M_FORBIDDEN and a misleading knock fallback: Matrix space membership never implied room access. restricted is the spec mechanism that makes "member of the server" mean "can join its channels", like Discord.

Gate

build clean, 471 unit tests (+1 skip), oxlint 0 errors, full Playwright e2e 14/14 green (13 existing + new e2e/tests/restricted-rooms.spec.ts: a space member auto-joins a restricted room with no invite). All run in this branch's worktree.

Based on main @ 65fdb32.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

🤖 Generated with Claude Code

Discord-style automatic room access for space members, picked from the 2026-09-16 live multi-user testing report ("click a link, you're on the server, and you're automatically in every room you have permission for — even rooms created later"). ## What this does - **New rooms created inside a space** now get a `restricted` join_rule with `allow: [{type: m.room_membership, room_id: <space>}]` (MSC3083): space membership = join permission, automatically, for members who join later and rooms created later. With the space's "allow join requests" toggle ON, the room gets `knock_restricted` instead (space members still auto-join; outsiders can knock). - **Space creation asks** for this policy ("Allow join requests from non-members", default **OFF**) with a plain-language hint, per the owner's request. - **Changeable later** in Space Settings (Access section). - Existing rooms are never silently rewritten — but the per-room join rule can be retrofitted from room settings. - Space hierarchy join flow (`SpaceHierarchyModal`) unchanged — restricted rooms join directly for space members, no invite needed. ## Why restricted and not invite/knock Plain `invite` join_rule (the previous default) is why the admin-added tester saw `M_FORBIDDEN` and a misleading knock fallback: Matrix space membership never implied room access. `restricted` is the spec mechanism that makes "member of the server" mean "can join its channels", like Discord. ## Gate build clean, 471 unit tests (+1 skip), oxlint 0 errors, full Playwright e2e **14/14 green** (13 existing + new `e2e/tests/restricted-rooms.spec.ts`: a space member auto-joins a restricted room with no invite). All run in this branch's worktree. Based on main @ 65fdb32. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> 🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: Discord-style auto room access for space members (restricted join_rule)
Some checks failed
CI / build-and-test (pull_request) Has been cancelled
CI / e2e (pull_request) Has been cancelled
8358dfc5a3
Real bug from live multi-user testing: JorKonvo rooms default to plain
"invite" join_rule, so space membership never granted access to the
space's rooms - everyone needed a per-room invite, even for rooms created
after they joined. An admin adding a user to a space via the Synapse admin
API found the user could see and join nothing (M_FORBIDDEN on join, and
on the knock fallback too, since the room wasn't knock-enabled either).

Per-room join_rule management (including restricted/knock_restricted) was
already fully built in RoomSettingsModal.tsx/joinRules.ts - what was
missing was applying it automatically at room creation time, and a
space-level policy to drive that default:

- spaceSettings.ts: new `allow_room_knocking` field (default off) plus
  isSpaceKnockingAllowed/setSpaceKnockingAllowed, following the same
  io.jorkonvo.space_settings pattern as the space's other toggles.
- room-admin.ts createRoom: a channel created inside a space (and not
  itself public) now gets join_rule "restricted" (or "knock_restricted"
  if the space allows outside knocking), allow-listing that space's
  membership - MSC3083, long-stable. Set via initial_state on the create
  call itself, not a follow-up sendStateEvent, so there's no local-echo
  race and no window where the room briefly has the wrong join_rule.
  Public rooms and existing rooms are unaffected.
- room-admin.ts createSpace: new allowRoomKnocking option, also set via
  initial_state at creation for the same race-free reason.
- CreateSpaceModal.tsx: a new "allow join requests from outside" toggle,
  default OFF, explained in one line. SpaceSettingsModal.tsx's Invites
  pane exposes the same toggle for changing it later.
- e2e/tests/restricted-rooms.spec.ts: proves the actual payoff - a user
  invited to a space only (never to a room, mirroring the real reported
  scenario) auto-joins a channel created in that space with a single
  click via the space hierarchy browser, no invite or knock needed.
  Added missing testids (hierarchy-room/-join/-open, discover-rooms-button)
  to drive that flow.

Gate: build/471 unit tests/0 lint errors/14-of-14 e2e all green. One
environmental finding along the way, documented in PLAN.md: e2e/docker-
compose.yml's fixed container and network names (jorkonvo-e2e-net,
e2e-caddy-1, e2e-synapse-1) collide when multiple worktrees run the e2e
suite concurrently - not a regression here (confirmed by rerunning this
worktree's stack in isolation), but a real gap for a future round.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BjDnbbumj61ftRMhpLXCKQ
claude closed this pull request 2026-09-16 18:34:28 +00:00
Author
Contributor

Merged into main via fast-forward merge commit 951294c (ort merge, no conflicts). Gate results on the merged tree: build clean, 471 unit tests passed (1 skipped), lint 0 errors, e2e 14/14 passed (including restricted-rooms.spec.ts). Closing.

Merged into main via fast-forward merge commit 951294c (ort merge, no conflicts). Gate results on the merged tree: build clean, 471 unit tests passed (1 skipped), lint 0 errors, e2e 14/14 passed (including restricted-rooms.spec.ts). Closing.
Some checks failed
CI / build-and-test (pull_request) Has been cancelled
CI / e2e (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
thatumi/JorKonvo!7
No description provided.