Overview
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 DID of the space's authority. Spaces created through HappyView use HappyView's service identity DID. See Space authority.
- 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: at://<space-did>/space/<type>/<skey>
A space record adds three more components to the URI: the author's DID, the collection NSID, and the record key:
at://<space-did>/space/<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:
const response = await fetch("http://127.0.0.1:3000/admin/settings/feature.spaces_enabled", {
method: "PUT",
headers: {
"Authorization": `Bearer ${TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ value: "true" }),
});When disabled, all space endpoints return a 404 error with FeatureDisabled as the error code.
Endpoints
Space endpoints are split across two namespaces:
com.atproto.space.*: protocol-level routes (queries, data, credentials, sync)com.atproto.simplespace.*: management routes (create/update/delete spaces, membership)
The dev.happyview.space.* aliases are deprecated and kept until v3. Most endpoints take DPoP authentication or cookie-based session auth. Read and sync endpoints also take a space credential, and the notify endpoints take service auth.
| Endpoint | Method | Description |
|---|---|---|
com.atproto.simplespace.createSpace | POST | Create a space |
com.atproto.simplespace.getSpace | GET | Get a space and its policies |
com.atproto.space.listSpaces | GET | List spaces by membership |
com.atproto.simplespace.updateSpace | POST | Update a space |
com.atproto.simplespace.deleteSpace | POST | Delete a space |
com.atproto.simplespace.putMember | POST | Add a member or set their access |
com.atproto.simplespace.removeMember | POST | Remove a member |
com.atproto.simplespace.listMembers | GET | List resolved members |
com.atproto.space.createRecord | POST | Create a record (auto-generated rkey) |
com.atproto.space.putRecord | POST | Write a record |
com.atproto.space.getRecord | GET | Get a record |
com.atproto.space.listRecords | GET | List records |
com.atproto.space.deleteRecord | POST | Delete a record |
com.atproto.space.applyWrites | POST | Batch write operations |
com.atproto.space.getLatestCommit | GET | Get per-user signed commit |
com.atproto.space.getRepo | GET | Export a user's repo as a CAR file |
com.atproto.space.listRepoOps | GET | List record operation log entries |
com.atproto.space.listRepos | GET | List the space's writer set |
com.atproto.space.getBlob | GET | Get a blob from a space |
com.atproto.space.listBlobs | GET | List the blobs a repo's records reference |
com.atproto.space.getDelegationToken | GET | Get a delegation token (step 1 of credentials) |
com.atproto.space.getSpaceCredential | POST | Exchange it for a space credential (step 2) |
com.atproto.space.registerNotify | POST | Register a syncer for write notifications |
com.atproto.space.unregisterNotify | POST | Withdraw a registration |
com.atproto.space.notifyWrite | POST | Report a repo's new commit |
com.atproto.space.notifySpaceDeleted | POST | Push a space-deleted notification |
dev.happyview.space.createInvite | POST | Create an invite (HappyView extension) |
dev.happyview.space.acceptInvite | POST | Accept an invite (HappyView extension) |
dev.happyview.space.revokeInvite | POST | Revoke an invite (HappyView extension) |
dev.happyview.space.listInvites | GET | List invites (HappyView extension) |
com.atproto.simplespace.addMember is a deprecated form of putMember, kept until v3. See Legacy addMember.
Access model
A space's creator administers it: they update and delete the space and manage its members and invites.
Two independent policies decide who can use the space. See Policies.
- The read policy decides who can obtain a space credential to read the whole space.
- The write policy decides whose writes the space tracks and forwards to syncers.
Each policy is member-list (the default), public, or managing-app.
App access controls which third-party apps can obtain credentials: open (the default) or an allow list of OAuth client IDs.
Under member-list policies, each member has two flags, read and write, set independently. The creator is automatically added with both. See Members.
Spaces also support delegation: adding another space as a member, which grants its members access to this space.
Alignment with Proposal 0016
HappyView implements atproto Proposal 0016 (Permissioned Data) with some HappyView-specific extensions.
Protocol features implemented
- Namespace split:
com.atproto.space.*for protocol routes,com.atproto.simplespace.*for management - Space authority: new spaces use the instance's DID, published with
#atproto_space_hostand#atproto_spaceentries - Read and write policies:
memberListPolicy,publicPolicy,managingAppPolicy - App access:
open,allowList - Member list:
putMemberwith independentreadandwriteflags - Delegation tokens:
getDelegationToken(GET, 60-second TTL, single use), or tokens signed by the account's own key - Space credentials:
atproto-space-credential+jwt, ES256, 10-minute TTL, bound to a key withcnf.kidand used with RFC 9421 HTTP Message Signatures - Credential revocation:
notifyCredentialRevokedsent to hosts of repos in the space - Deniable commit signatures: user signs context (space + author + rev + random IKM), not content hash
- LtHash: homomorphic set-hash (2048-byte state, 1024 uint16 lanes, BLAKE3 XOF)
- SignedCommit: versioned commit struct (
ver: 1) with hash, ikm, sig, mac, rev - Record operation log:
listRepoOpsreturns the oplog for sync (values inlined by default,excludeValuesto opt out) - Latest commit:
getLatestCommitreturns the signed commit for a user in a space - Repo export:
getRepoexports a user's repo as a CAR v1 file (signedCommit + DRISL index) - Sync:
registerNotifyby service identifier,notifyWritein both directions,listReposfrom the writer set with a space-wide revision - Space-scoped blobs:
getBlob,listBlobs
HappyView extensions (not in the protocol spec)
- Invite system:
createInvite,acceptInvite,revokeInvite,listInvites(underdev.happyview.space.*) isDelegationon members: allows spaces to be members of other spacesdisplayName,descriptionon spaces: human-readable metadataconfigobject:membership_public, plus arbitrary extra fields.records_publicis deprecated and not enforced.read_selfaccess: limits a member's reads to their own records
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
- Write Notifications: keep syncers up to date
- Invites: invite-based membership
- Changelog: version history