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.
// lexicons.ts
import { defineLexicons, field } from 'airspace/lexicon'
export default defineLexicons('dev.roe', {
note: {
title: field.text(),
body: field.markdown(),
createdAt: field.datetime(),
},
})
// notes.ts
import { createAirspace, defineCollection, passwordSession } from 'airspace'
import lexicons from './lexicons.ts'
const notes = defineCollection(lexicons.note)
export const airspace = createAirspace({
identity: 'roe.dev',
collections: { notes },
session: await passwordSession({ service: 'https://pds.example', identifier: 'roe.dev', password: process.env.PDS_APP_PASSWORD! }),
})
const published = await airspace.notes.list({ limit: 10 })
await airspace.notes.create({ title: 'Hello', body: '# hi', createdAt: new Date().toISOString() })
[!WARNING]
Permissioned spaces, where drafts and private records live, 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.
examples/nuxt: a Nitro plugin providing an airspace client + server routes +@comark/vue.examples/astro: static generation + getStaticPaths + Astro actions for writing.examples/sveltekit: load functions + form actions with field-level validation.examples/node: a CLI with no framework and no build step.The demo on getair.space is the same app, running against a sandbox account on a demo PDS.
pnpm installpnpm dev. They run against a real PDS with permissioned spaces, booted in process with @atproto/dev-envpnpm dev:pds for a PDS on port 2583 that stays up, printing dotenv-shaped credentials for two accountspnpm lex:build to regenerate src/lex from lexicons/pnpm size to print the bundle size table in the docsMany thanks to @mkrl for the name idea 🙏
Made with ❤️
Published under MIT License.