diff --git a/proposals/1772-groups-as-rooms.md b/proposals/1772-groups-as-rooms.md
index 4a0d7aed..76520b92 100644
--- a/proposals/1772-groups-as-rooms.md
+++ b/proposals/1772-groups-as-rooms.md
@@ -187,7 +187,8 @@ TODO: this could also be done via pinned messages. Failing that
### Managing power levels via spaces
-XXX: this section still in progress
+TODO: much of this is orthogonal to the headline feature of "spaces", and
+should be moved to a separate MSC.
One use-case for spaces is to help manage power levels across a group of
rooms. For example: "Jim has just joined the management team at my company. He
@@ -234,21 +235,21 @@ key which is an ordered list, for example:
```js
{
- "type": "m.room.power_level_mappings",
- "state_key": "",
- "content": {
- "mappings": [
- {
- "space": "!mods:example.org",
- "via": ["example.org"],
- "power_level": 50
- },
- {
- "space": "!users:example.org",
- "via": ["example.org"],
- "power_level": 1
- }
- ]
+ "type": "m.room.power_level_mappings",
+ "state_key": "",
+ "content": {
+ "mappings": [
+ {
+ "space": "!mods:example.org",
+ "via": ["example.org"],
+ "power_level": 50
+ },
+ {
+ "space": "!users:example.org",
+ "via": ["example.org"],
+ "power_level": 1
+ }
+ ]
}
}
```
@@ -283,22 +284,97 @@ access to it is itself restricted via `power_levels`. This could be enforced by
the admin bot so that no `m.room.power_levels` events are generated unless
`power_level_mappings` is appropriately restricted.
-### Membership restrictions
+### Restricting room membership based on space membership
A desirable feature is to give room admins the power to restrict membership of
-their room based on the membership of spaces (for example, "only members of the
-#doglovers space can join this room"[1](#f1)).
-
-XXX can we maybe do this with invites generated on demand? If not, we probably
-need some sort of "silent invite" state for each user,
+their room based on the membership of spaces (for example, "members of the
+#doglovers space can join this room without an invitation"[1](#f1)).
-By implication, when a user leaves the required space, they should be ejected
-from the room.
+We could represent the allowed spaces with additional content in the
+`m.room.join_rules` event. For example:
-XXX: how do we implement the ejection? We could leave it up to the ejectee's
-server, but what happens if it doesn't play the game? So we probably need to
-enact a ban... but then, which server has responisiblity, and which user is used?
+```js
+{
+ "type": "m.room.join_rules",
+ "state_key": "",
+ "content": {
+ "join_rule": "public",
+ "allowed_spaces": [
+ {
+ "space": "!mods:example.org",
+ "via": ["example.org"],
+ },
+ {
+ "space": "!users:example.org",
+ "via": ["example.org"],
+ }
+ ]
+ }
+}
+```
+XXX: would it be better to put it in a separate event? Doing so would probably
+require us to come up with a new `join_rule` state to tell servers to go and
+look for the allowed spaces.
+
+The `allowed_spaces` key applies a restriction to the `public` join rule, so
+that only users in those spaces should be allowed to join. Additionally, users
+who have received an explicit `invite` event are allowed to join[2](#f2). If the `allowed_spaces` key is an empty list (or not a
+list at all), no users are allowed to join without an invite.
+
+Unlike the regular `invite` join rule, the restriction cannot be enforced over
+federation by event authorization, so servers in the room are trusted not to
+allow invalid users to join.[3](#f3)
+
+When a server receives a `/join` request from a client or a
+`/make_join`/`/send_join` request from a server, the request should only be
+permitted if the user has a valid invite or is in one of the listed spaces
+(established by peeking).
+
+XXX: redacting the join_rules above will reset the room to public, which feels dangerous?
+
+A new room version is not absolutely required here, but may be advisable to
+ensure that servers that do not support `allowed_spaces` do not join the room
+(and would also allow us to tweak the redaction rules to avoid the foot-gun).
+
+#### Kicking users out when they leave the allowed_space
+
+In the above example, suppose `@bob:server.example` leaves `!users:example.org`:
+they should be removed from the room. One option is to leave the departure up
+to Bob's server `server.example`, but this places a relatively high level of trust
+in that server. Additionally, if `server.example` were offline, other users in
+the room would still see Bob in the room (and their servers would attempt to
+send message traffic to it).
+
+Instead, we make the removal the responsibility of the room's admin bot (see
+above): the bot is expected to peek into any `allowed_spaces` and kick any
+users who are members of the room and leave the union of the allowed
+spaces.
+
+(XXX: should users in a space be kicked when that space is removed from the
+`allowed_spaces` list? We think not, by analogy with what happens when you
+switch the join rules from `public` to `invite`.)
+
+One problem here is that it will lead to users who joined via an invite being
+kicked. For example:
+ * `@bob:server.example` creates an invite-only room.
+ * Later, the `join_rules` are switched to `public`, with an `allowed_space` of
+ `!users:example.org`, of which Bob happens to be a member.
+ * Later still, Bob leaves `!users:example.org`.
+ * Bob is kicked from his own room.
+
+Fixing this is thorny. Some sort of annotation on the membership events might
+help. but it's unclear what the desired semantics are:
+
+ * Assuming that users in a given space are *not* kicked when that space is
+ removed from `allowed_spaces`, are those users then given a pass to remain
+ in the room indefinitely? What happens if the space is added back to
+ `allowed_spaces` and *then* the user leaves it?
+
+ * Suppose a user joins a room via a space (SpaceA). Later, SpaceB is added to
+ the `allowed_spaces` list and SpaceA is removed. What should happen when the
+ user leaves SpaceB? Are they exempt from the kick?
## Future extensions
@@ -370,6 +446,13 @@ These dependencies are shared with profiles-as-rooms
* The peek server has significant power. TODO: expand.
+* The `allowed_spaces` feature places increased trust in the servers in the
+ room. We consider this acceptable: if you don't want evil servers randomly
+ joining spurious users into your rooms, then a) don't let evil servers in
+ your room in the first place, b) don't use `allowed_spaces` lists, given the
+ expansion increases the attack surface anyway by letting members in other
+ rooms dictate who's allowed into your room".
+
## Tradeoffs
* If the membership of a space would be large (for example: an organisation of
@@ -442,3 +525,14 @@ of the '#catlovers' space" is less useful since (a) users in the banned space
could simply leave it at any time; (b) this functionality is already somewhat
provided by [Moderation policy
lists](https://matrix.org/docs/spec/client_server/r0.6.1#moderation-policy-lists). [↩](#a1)
+
+[2]: Note that there is nothing stopping users sending and
+receiving invites in `public` rooms today, and they work as you might
+expect. The only difference is that you are not *required* to hold an `invite`
+when joining the room. [↩](#a2)
+
+[3]: This is a marginal decrease in security from the current
+situation with invite-only rooms. Currently, a misbehaving server can allow
+unauthorized users to join an invite-only room by first issuing an invite to
+that user. In theory that can be prevented by raising the PL required to send
+an invite, but in practice that is rarely done. [↩](#a2)