Skip to content

Collaboration sessions: invite roles, identity binding, durable overrides, moderation, and layer locks #1681

Description

@giswqs

Summary

Move collaboration sessions from anonymous-with-a-host-token to identity-aware: per-invite roles, participants optionally bound to a signed-in account, host moderation (kick and block), per-layer locks, and restricting session creation to the app's own origins.

Why

The session model documented in docs/collaboration.md is a good MVP and already gets the important part right — permissions are enforced in the Durable Object, the relay assigns clientId server-side so a participant cannot claim another's identity, and set-mode / set-participant-mode require the host token. The gaps are the ones the MVP knowingly deferred.

  • Anonymous by construction. A joiner picks any display name. Two people can be "Alex"; nothing ties a participant to an account. For a classroom or a client review, the host cannot tell who is who.
  • Overrides do not survive reconnect. Because they are keyed to the per-socket clientId, a guest pinned to view-only who drops and rejoins reverts to the session default. The docs call this acceptable for the MVP; it is the wrong behavior once the override is being used as a real restriction, since the workaround is simply to reconnect.
  • Permission is session-wide or per-socket, never per-object. A host cannot say "this basemap and this reference layer are locked; edit the rest."
  • No moderation. There is no kick, and no way to stop someone from rejoining a session whose code they hold.
  • Session creation is open. The docs flag this explicitly: POST /sessions is unauthenticated and responds with Access-Control-Allow-Origin: *, so any page can create sessions against the relay.

Proposal

1. Restrict session creation — the operator note in docs/collaboration.md, done. Allowlist the app's own origins on POST /sessions, and rate-limit creation per origin. This is small, independent, and worth doing on its own.

2. Invite links with a baked-in role — instead of one session code plus manual per-participant adjustment, let the host mint invites: a view-only invite and a co-edit invite, each revocable. The role travels with the link, so the host does not have to demote every joiner by hand.

3. Optional identity binding — a session flag requiring participants to be signed in, in which case the display name is the account name and the relay attributes edits to it. Anonymous sessions stay supported and stay the default; the workshop-with-a-code case is the one this feature exists for.

4. Durable overrides — key per-participant permission to the participant identity (account, or invite token for anonymous sessions) rather than the socket, so a reconnect preserves it. Persist alongside latestSnapshot and rev rather than only on the socket attachment.

5. Moderation — kick a participant, and block their invite token or account from rejoining for the session's lifetime. Reuse the host-token gate that already protects set-mode.

6. Per-layer locks — the host marks layers locked; the relay rejects an inbound snapshot that changes a locked layer, the same way it already drops a snapshot from a guest who cannot edit. Note that whole-snapshot last-write-wins makes this a diff against the stored snapshot, so scope the comparison carefully.

Scope notes

  • Keep the shared client/server permission helper (participantCanEdit() in lib/collab-protocol.ts) as the single definition of effective permission; every addition here extends that function on both sides rather than adding a second rule.
  • The relay must stay the authority. Any of this enforced only in the client UI is decoration.
  • Item 1 is a standalone small change and should not wait for the rest.

Effort

Medium overall; item 1 is small.


Part of #1665 (access control umbrella).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancement (medium)Broader feature, feasible within a reasonable timeframe

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions