spaces
Anything in your repo is public straight away, which is no good for drafts. A permissioned space is a private area of the same repo, using the same collections and schemas.
Spaces are experimental. They need a PDS running prerelease software: atproto'spermissioned-databranch, or the@atproto/pdsspaces alpha. Hosted PDSes, includingbsky.social, do not support them yet. The API may change.
drafting and publishing
Declare a space with space([...]) and defineSpace, as in your content model. Its collections then have the same API public ones do:
const draft = await airspace.workspace.projects.create({ name: 'Not public yet', category: ref, createdAt: now })
await airspace.workspace.projects.publish(draft.rkey) // copies to the public repo at the same key
await airspace.workspace.projects.publish(draft.rkey, { transform: value => ({ ...value, publishedAt: now }) })
await airspace.workspace.projects.publish(draft.rkey, { ifMatch: draft.cid })
const live = await airspace.workspace.projects.published() // keys that exist in both
await airspace.workspace.supported() // false on most PDSes todayA published copy is identical to its draft, down to the content hash, so ifMatch: draft.cid means "only if nobody has edited the public record since". Pointers between drafts are stored as plain AT URIs, so a published copy keeps them.
Without support, a space call throws SpacesUnsupportedError.
managing a space
airspace.workspace.manage creates a space and sets who can see it. Reading and writing are set separately, each to 'public', 'member-list' or { managingApp: did }:
await airspace.workspace.manage.ensure({ read: 'member-list', write: 'member-list', appAccess: 'open' })
await airspace.workspace.manage.update({ read: 'public' })
await airspace.workspace.manage.members.add(did) // read and write, unless you pass { read, write }
await airspace.workspace.manage.members.list() // [{ did, read, write }]
await airspace.workspace.manage.exists() // is the space there at all?
await airspace.workspace.manage.info() // how is it configured? `null` until `ensure()`You only need ensure() for a shared space. Writing to your own space creates it but leaves it unconfigured.
Blobs uploaded into a space are currently readable through the public sync.getBlob endpoint (atproto#5435). Don't put images in a space that you'd mind being seen.