SvelteKit starter for building distinctive web apps on AT Protocol.
This template is intentionally client-only and static-first. It gives you real browser OAuth, public Bluesky reads, authenticated record writes, direct PDS reads, and reusable ATProto service modules so you can build an app around your own lexicons and interaction model.
AT Protocol is the backend. There is no server in this repo.
@atcute/oauth-browser-clientnpm install
npm run dev
Open http://127.0.0.1:4173.
The app is organized so developers can keep the included routes, replace them, or build entirely new experiences on the same AT layer.
src/lib/at/oauth.svelte.ts
Browser OAuth bootstrap, session restore, and current-user statesrc/lib/at/auth.ts
Authenticated session helpers for write operationssrc/lib/at/client.ts
Public client and per-DID PDS clientssrc/lib/at/did.ts
Handle resolution and DID document lookupsrc/lib/at/records.ts
Generic CRUD helpers for custom collectionssrc/lib/at/services/profiles.ts
Public profile reads and lightweight cachingsrc/lib/at/services/follows.ts
Follow state lookup and follow/unfollow writessrc/lib/at/services/posts.ts
Post creation for app.bsky.feed.postsrc/lib/at/services/notes.ts
Example custom-collection CRUD for com.example.app.notesrc/lib/at/services/repo.ts
Repo description and collection preview loadingsrc/routes/*
Example pages that consume the ATProto service layer rather than issuing raw XRPC calls inline/ home/login sign in / sign up/search actor search/profile/[id] profile + follow button/post publish a basic Bluesky post/notes custom collection example for com.example.app.note/repo inspect your repo collections and open records in pdsls.devFor most ATProto apps, the central workflow is:
That means you should treat the bundled routes as examples, not constraints.
Good use cases for this starter:
com.yourdomain.app.note.lexicons/ with the same id.repo:com.yourdomain.app.note to the app scope.createRecord, putRecord, getRecord, and listRecordsForRepo from src/lib/at/records.ts.This starter includes a complete reference implementation:
lexicons/com.example.app.note.jsonsrc/lib/at/services/notes.tssrc/routes/notes/+page.svelteExample record shape:
await createRecord('com.yourdomain.app.note', {
$type: 'com.yourdomain.app.note',
text: 'hello world',
createdAt: new Date().toISOString()
});
Recommended pattern:
src/lib/atsrc/lib/your-appThe included notes example is the core extension pattern for this starter.
It shows how to:
listMyNotes, createNote, and deleteNoteIf you are building your own app concept, this is usually the first pattern to copy and adapt.
static/client-metadata.json127.0.0.1repo:com.example.app.note$type and RFC3339 createdAtmain.GitHub Actions.BASE_PATH and SITE_ORIGIN.build/.The build step also generates static/client-metadata.json.
This repo also includes a GitHub Actions workflow that mirrors main to Tangled:
.github/workflows/mirror-tangled.yml[email protected]:charlebois.info/sveltekit-atproto-starterTo enable it:
edit .github/workflows/mirror-tangled.yml and replace charlebois.info with your Tangled handle
keep the repo name or change it to your own Tangled repo path
add this GitHub Actions secret in your repository settings:
TANGLED_SSH_KEY
An SSH private key that has push access to the Tangled repo
Once that secret is set, every push to main on GitHub will mirror the same commit to Tangled.
npm run dev start local dev servernpm run check run Svelte and TypeScript checksnpm run build generate client metadata and build the static sitenpm run preview preview the production buildATProto apps usually need the same difficult pieces:
This repo keeps those pieces in place so you can spend time on the actual experience you want to invent.