Skip to main content

Permissioned Spaces

Experimental

Permissioned Spaces are experimental and the API will change. This implementation follows Daniel Holmgren's Permissioned Data Diaries and aligns structurally with the permissioned-data branch on bluesky-social/atproto, but uses a dev.happyview namespace to allow iteration while the official spec stabilizes.

Spaces are containers for permissioned data in atproto. Unlike regular public records that live in a user's repo, space records are gated by membership — only members can read or write data within a space.

Concepts

A space is identified by three components:

  • Space DID — the space's own decentralized identifier (for personal spaces, this is the user's DID)
  • Type — the space type as an NSID, describing the modality (e.g. a forum, a group chat, a photo album)
  • Space key (skey) — a short string differentiating multiple spaces of the same type

These form the space URI: ats://<space-did>/<type>/<skey>

A space record adds three more components to the URI: the author's DID, the collection NSID, and the record key:

ats://<space-did>/<type-nsid>/<skey>/<author-did>/<collection>/<rkey>

Feature flag

In HappyView, spaces are gated behind the feature.spaces_enabled instance setting. Enable it in the dashboard under Settings or via the admin API:

curl -X PUT http://127.0.0.1:3000/admin/settings/feature.spaces_enabled \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"value": "true"}'

When disabled, all /xrpc/dev.happyview.space.* endpoints return 501 Not Implemented.

Endpoints

All space endpoints live under the dev.happyview.space namespace and require DPoP authentication.

EndpointMethodDescription
dev.happyview.space.createSpacePOSTCreate a space
dev.happyview.space.getSpaceGETGet a space by URI
dev.happyview.space.listSpacesGETList spaces by membership
dev.happyview.space.updateSpacePOSTUpdate space metadata
dev.happyview.space.deleteSpacePOSTDelete a space
dev.happyview.space.createRecordPOSTCreate a record (auto-generated rkey)
dev.happyview.space.putRecordPOSTWrite a record
dev.happyview.space.getRecordGETGet a record
dev.happyview.space.listRecordsGETList records
dev.happyview.space.deleteRecordPOSTDelete a record
dev.happyview.space.applyWritesPOSTBatch write operations
dev.happyview.space.addMemberPOSTAdd a member
dev.happyview.space.removeMemberPOSTRemove a member
dev.happyview.space.listMembersGETList resolved members
dev.happyview.space.createInvitePOSTCreate an invite
dev.happyview.space.redeemInvitePOSTRedeem an invite
dev.happyview.space.revokeInvitePOSTRevoke an invite
dev.happyview.space.listInvitesGETList invites
dev.happyview.space.getMemberGrantPOSTProve membership (step 1)
dev.happyview.space.getSpaceCredentialPOSTGet a space credential (step 2)

Access model

Spaces have an access mode that controls third-party app access:

  • default_allow — any app can access (with optional denylist)
  • default_deny — only explicitly allowed apps can access

Individual users access spaces through membership. Members have either read or write access. Write access implies read. The space creator is automatically added as a write member.

Spaces also support delegation — adding another space as a member, which transitively grants access to all members of the delegated space.

Divergences from the reference spec

HappyView mostly mirrors Daniel Holmgren's permissioned-data branch but diverges in some areas. These will narrow as the official spec stabilizes.

HappyView extensions (not in the reference branch)

  • isDelegation on members allows spaces to be members of other spaces
  • displayName, description, accessMode on spaces — the reference space model is minimal (uri, isOwner, isMember, createdAt)
  • appAllowlist / appDenylist / managingAppDid — app-level access control layer
  • config object on spaces (e.g. membershipPublic, recordsPublic)
  • Invite systemcreateInvite, redeemInvite, revokeInvite, listInvites
  • read / write access levels — the reference branch treats membership as binary

Reference features not yet implemented

  • OplogsgetRepoOplog, getMemberOplog, getRepoState, getMemberState (sync primitives for space data)
  • Push notificationsnotifyWrite, notifyMembership (service-to-service event delivery)
  • Space-scoped blobsuploadBlob for blobs within a space context
  • Owner record deletion — in the reference branch the space owner can delete any record; HappyView restricts deleteRecord to the record's author only

Next steps

  • Managing Spaces — create, update, and delete spaces
  • Members — manage membership and delegation
  • Records — read and write permissioned data
  • Credentials — cross-service authentication for spaces
  • Invites — invite-based membership