the database you already have.
every Bluesky atmosphere account comes with a personal data server: a cms, login, file storage and a public API that you own.
pnpm add airspaceNode 22+
define a model
lexicons in TypeScript, in your own namespace.
// lexicons.ts
import { defineLexicons, field, space } from 'airspace/lexicon'
export default defineLexicons('dev.roe', {
note: {
title: field.text({ max: 120 }),
body: field.markdown(),
createdAt: field.datetime().optional(),
},
workspace: space(['note']),
})// collections.ts
import { defineCollections, defineSpace } from 'airspace'
import lexicons from './lexicons.ts'
export const { note: notes } = defineCollections(lexicons, {
note: { sort: [['createdAt', 'desc']] },
})
export const workspace = defineSpace(lexicons.workspace, {
collections: { notes },
})access your data
a fully typed client over your repo and your drafts.
// airspace.ts
import { createAirspace } from 'airspace'
import { workspace } from './collections.ts'
export const airspace = createAirspace({
identity: 'roe.dev',
spaces: { workspace }, // its `notes` is a public collection too
session, // omit for read-only
})
const published = await airspace.notes.list({ limit: 5 })
const draft = await airspace.workspace.notes.create({
title: 'Not public yet',
body: '# hello',
})
await airspace.workspace.notes.publish(draft.rkey)what you get
- you own your data
- Records live in your own repo under your own schema. Switch tools or hosts – or even stop using airspace – and everything still works.
- no database to run
- Reads, writes, auth and images are all handled by your own PDS. airspace is a typed client.
- drafts built in
- Experimental permissioned spaces for private data, with a single call to publish.
- typed from your schema
- Define the model once. Records, keys, joins and OAuth scopes are inferred magically. ✨
works with what exists
airspace manages any collection you have a lexicon for, not only the ones you wrote.
com.whtwnd.blog.entryWhiteWind posts, drafted in a space and published to your public repo.site.standard.*Publications and documents, read from a live repo.community.lexicon.calendar.*Events and RSVPs, joined across two accounts.your.own.lexiconWritten withdefineLexicons, or brought in as JSON.
Nothing in your repo carries an airspace $type or is proprietary to airspace.
if you already know atproto
airspace sits between @atproto/lex-schema and your site.
Lexicons are TypeScript, and your public repo and your permissioned spaces share one typed API. airspace lexicons emit writes the JSON when you want to publish your schemas. airspace itself defines no content model, renders nothing and hosts nothing.