Groups

Group management

1 Group membership lifecycle

stateDiagram-v2
[*] --> requests: entry request
[*] --> seat: join w/token
[*] --> pending: admin invitation
requests --> seat: approve
pending --> seat: join
seat --> [*]: leave
requests --> [*]: leave
pending --> [*]: admin delete

2 Foreign group lifecycle

A foreign group is a group that we possess some information about but that we are not a full member of yet.

A minimal information we can have about a group, apart from knowing its existence, is a group preview. Obtaining a group preview is non-obligatory – we might want to preview groups we have no intention to join. A group preview can be requested of public and private groups without any permissions, while previewing a secret group requires a valid access token. Note: secret group previews currently remain unimplemented.

There are two ways to join a group: we can ask the group host for entry, or we can attempt to join directly, possibly with an access token. A successful entry request yields a group access token received from the host, that is then used to join the group.

A group join has two stages: in the first stage we poke the group host with access token. If successful, we are granted a seat in the group. Subsequently, we subscribe to the group host on two paths: general update path, and a ship specific update path that is used to communicate facts not meant for everyone (such as admin-only facts, or personal tokens sent only to the requester). Note: currently all communications happens on personal update paths.

NOTE: how can we distinguish between a kick terminating response, and a kick due to other conditions? It seems there is no reliable way to do that. But we could issue a pre-emptive leave on our own if we received a response. This way a kick can only signal a problem.

2.1 Foreign group lookup

stateDiagram
[*] --> preview: subscribe for preview
preview --> error
preview --> kick
preview --> done
done --> preview: refresh
kick --> preview: retry after delay
error --> preview: retry

2.2 Foreign group join progress

stateDiagram
[*] --> ask: ask for entry
ask --> X1: cancel
ask --> error1: poke fail
ask --> error1: watch fail
ask --> join: token fact
ask --> join: user join 

[*] --> join: user join
join --> watch: poke ack
join --> error2: poke fail
join --> X2: cancel

watch --> done: join completed
watch --> X3: cancel
watch --> error3: watch fail 

3 Sequences

3.1 Ask request

3.1.1 Public group ask

An ask request issued to a public group is automatically approved, unless the client has been banned. The group host generates a new invite token and sends it to the client, which then proceeds to join the group with the received access token.

sequenceDiagram
participant client
participant host

client ->> host: %ask poke
client ->> host: %watch /ask/ship

alt permission denied
	host->>client: poke nack
	host->client: watch nack
else
	host->>client: poke ack
	host->host: generate token
	host->>client: watch ack
	host->>client: %give token
	client->>host: join w/ token
end

3.1.2 Private group ask

An ask request issued to a private group is recorded in requests.admissions, unless the ship has been banned. Group admins can then approve or deny requests. For an approved request, the group host issues a token that is then send to the client. The client then uses the token to join the group. If an ask request has been denied, the group host notifies the client by kicking his subscription, which terminates the flow at the client side.

3.1.2.1 Client ask

sequenceDiagram
participant client
participant host

client ->> host: %ask poke
client ->> host: %watch /ask/ship

alt permission denied
	host->>client: poke nack
	host->>client: watch nack
else
	host->>client: poke ack
	host->>client: watch ack
	host->host: record request
end

3.1.2.2 Admin approve and deny

sequenceDiagram
participant client
participant host
participant admin
opt request approved
	admin->>host: approve the request
	host->host: delete the record
	host->host: generate new token
	host->>client: %give token
	host->>client: %kick
	client->>host: join w/token
end
opt request denied
	admin->>host: deny the request
	host->host: delete the record
	host->>client: %kick
end

3.2 Group join request

3.3 Group leave

A client agent is in some way registered by the group host in the following cases: (1) he is already a group member, (2) he is in the process of joining the group and has a registered seat, (3) he has issued a group ask request, (4) he has been added to the pending list.

If the client then wishes to forfeit that registration, he can issue a group leave request. The group host will then de-register the client from all existing records. In particular, the host will do the following.

  1. Delete the client’s seat and kicks any outstanding group subscriptions.
  2. Delete the client hosted channels.
  3. Delete the client’s ask request and kicks any outstanding ask subscriptions.
  4. Delete the the client from the pending list.
sequenceDiagram
participant client
participant host

client ->> host: %leave poke
opt is member
	host->host: delete the seat
	host->>client: %kick on /updates/time
	host->>client: poke %ack
end

opt is asking
	host->host: delete the request
	host->>client: %kick on /ask/ship
	host->>client: poke %ack
end

opt is pending
	host -> host: delete from pending
	host->>client: poke %ack
end

3.4 Channel preview

Channel previews carry the metadata of a channel, together with the associated group preview. Q: can a channel belong to two groups at once? Do we guard against it? This is useful because in some contexts our starting point are the channels, which do not, by themselves, carry associated group information. Imagine we want to display a list of channels that have unread notifications. We know their identity, but on the display list we would also like to show the owning group icon etc. A channel preview enables us to store all required information in one place, and resolve it, if it is yet missing.

sequenceDiagram

participant ca as client A
participant host as group host
participant cb as client B

ca ->> host: watch /v1/channels/$nest/preview
alt hosts channel
host ->> host: verify read permissions
host ->> ca: %give channel preview
else
host ->> cb: watch /v1/channels/$nest/preview
cb->> cb: verify read permissions
cb->> host: %give channel preview
host ->> ca: %give channel preview
end

4 Permissions

4.1 Group management

Admin rights are required to issue group management actions. A role can be designated as having admin rights, and any user being assigned the role is considered an admin. A newly created group has a default admin role with id %admin.

The following actions are admin-restricted:

  1. Updating group metadata
  2. Changing group entry policy
  3. Membership management
  4. Roles management
  5. Channel management
  6. Section manegement

Only the group host is allowed to assign or revoke admin rights to a role. However, any admin is able to assign and revoke roles, including admin roles.

The group host is always considered to possess admin rights required to execute a group action, irrespective of any assigned roles.

Note: should we restrict assignment of admin-enabled roles to the group host? Otherwise two admins could try to revoke each other’s rights to manage the group in an attempt to gain control of the group.

4.2 Channel permissions

A channel has read, write permissions which can be assigned to specific roles. If no roles has been assigned a given permission, it means the permission is granted to all group members.

Due to somewhat unfortunate architectural reasons, channel read and write permissions are stored in two separate places. The read permissions are stored in the groups agent, while the write permissions are stored in the channels-server. The reason for this is that when a group member wants to join a channel, it makes the decision based on the information available in the client group data, which includes read permissions. Write permissions, on the other hand, are only checked when a user attempts to post a message. This action is directed to the channels-server agent, which can verify the write permissions without needing to query external data.