Collaboration/Overview

Collaboration

A space is the unit of sharing: one encrypted CRDT world with its own access-control list. Everything on these pages is about who is in a space, how they got there, what they may do, and how several devices or members converge on the same objects without a coordinator deciding for them.

Four ways in

  • A team space: create an invite, the newcomer requests to join, an owner approves — Invites.
  • A known account: grant its identity through the ACL; the recipient accepts the incoming space before loading it — Access control.
  • Two people: the deterministic one-to-one derivation, no invite at all — One-to-one spaces.
  • Readers without approval: a guest key, read-only for anyone holding it — Guest access.

A successful join request means approval is pending, not that the space is ready to query: read membership and space status before showing shared data.

The model in one picture

owner ── mints invite ──► token ──► joiner: POST /v1/spaces/join  (status: joining)
   │                                            │
   └── acl/accept {requestRecordId, permission} ─┘  ──► member (status: active)

owner ── acl/add {identity, permission} ──► added account sees invite_pending ──► invite/accept

me ── one-to-one {otherIdentity} ──► both derive the same space, both writers
Concept What it is Page
Member an identity on a space's ACL with a permission and a status Members and roles
Invite a shareable token that lets someone request to join, or a public read-only guest key Invites
ACL operation owner/admin actions: accept, decline, change permissions, remove, add by identity, transfer ownership, stop sharing ACL
One-to-one space a two-identity space derived from both account keys — no invite, no owner One-to-one
Derived space a well-known per-account space every device resolves to the same id Derived spaces
Bundle one root object per install registered in the space, so members converge on one chat, one setup; the server's usecase catalog installs the well-known apps Bundles

Roles

Permission Can
owner everything, including ACL operations and ownership transfer
admin manage members
writer read and write content
reader read content
guest read-only via a shared public guest key; writes return 403 space.read_only
none no access (also "not mirrored yet" on SpaceInfo.ownRole)

Your own role in every space is mirrored onto the space list as SpaceInfo.ownRole, so a UI can gate role-dependent controls straight from GET /v1/spaces — no per-space fan-out. GET /v1/spaces/:id/members/me is the authoritative read when it matters.

Why it matters. Access control is cryptographic, not a policy a server enforces: a grant hands the member the space's read key, a removal rotates it for every change from then on (what the member already received stays decryptable). A relay that stores the space cannot read it, and cannot quietly add itself as a member.

Convergence without a coordinator

Two themes recur across the section:

  • Derive instead of create when several parties must land on the same object. A one-to-one space id is a function of both account keys; a derived space id is a function of the account keys and a fixed seed; a bundle root can be derived from its bundle id. Nothing to race, nothing to reconcile.
  • Approval is local when the cryptography cannot gate it. A one-to-one ACL is immutable and a direct-add already puts you on the ACL, so "accept" governs whether this device materializes the space, surfaced as a space status, not an ACL write.