Atmos — building a professional creative network on AT Protocol (architecture notes)

Hi all — I’m Dave, founder of Atmos.

Erlend Sogge Heggen pointed me toward this community and suggested I share what we’re building. Wanted to post some architecture notes from building on AT Protocol, since a few of the design decisions might be useful to the broader conversation here.

What Atmos is

A professional network for creatives — designers, photographers, illustrators — that combines portfolio, social feed, CV, and professional tools under one sovereign identity at @yourname.atmos.cv. Currently in a founding-creator phase.

The stack

  • Reference PDS on Fly.io with PDS_HOSTNAME=atmos.cv

  • Identities are did:plc: registered with plc.directory

  • Standard lexicons only: app.bsky.feed.post, app.bsky.graph.follow, app.bsky.feed.like

  • Web app on Cloudflare Pages, PDS proxied through a Cloudflare Worker on the same domain

  • No custom lexicons yet (more on this below)

Dual-write architecture (ATProto-first)

Atmos writes to both a PDS and Firebase, with ATProto as the source of truth. Posts are written to the PDS first — facets, compressed images, the full app.bsky.feed.post record. Only on PDS success does the mirrored Firestore document get created, carrying the atUri as a reference. Follows and likes sync to the PDS via background triggers. If the PDS write fails, nothing gets written. No partial state.

Images are compressed to fit PDS blob limits; full-quality versions go to Firebase Storage with a blobCids map linking the two.

Federation

Federation works today. The Bluesky relay crawls our PDS — Atmos posts are visible on Bluesky’s AppView. In the other direction, Atmos reads from Bluesky’s public API to display federated profiles and posts from across the network. Atmos users can see and interact with Bluesky users, and vice versa.

Account migration (Bluesky → Atmos)

We have a working migration flow: export the repo via com.atproto.sync.getRepo, migrate blobs, create an account on the Atmos PDS with the same DID, import the repo, then rotate the signing key via PLC operation. The user’s identity survives the move — their DID doesn’t change, just where it points.

The lexicon question

Atmos has record types that don’t map to app.bsky.*: portfolio entries, CV/work-history records, and inspiration boards (curated collections). Right now these live entirely in Firebase — not on the PDS, not federable, not portable.

The question I’d love input on: should apps like Atmos define their own lexicons (e.g. cv.atmos.portfolio.entry) for domain-specific records? Or is there value in a shared professional-profile lexicon that multiple apps could interoperate on? This feels directly relevant to the taxonomy conversations happening on this forum.

A note for builders proxying a PDS behind Cloudflare

If your web app and PDS share a domain through a Cloudflare Worker, a few things we hit in production:

  • Cloudflare strips Sec-Fetch-* headers before the worker sees them. The PDS OAuth provider needs Sec-Fetch-Site to render the authorize page — you’ll need to synthesize it in the worker.

  • Using redirect: 'follow' in the proxy swallows PDS 303 redirects and Set-Cookie headers. Use redirect: 'manual'.

  • The PDS serves its own OAuth UI assets at /@atproto/oauth-provider/~assets/* — make sure your worker routes those to the PDS, not your web app.

Happy to share the worker code or dig into any of this further.

Handles are live — you can reserve @yourname.atmos.cv now at atmos.cv. Your identity is a real did:plc: on our PDS, registered with plc.directory. It’s yours to keep even if Atmos disappears.

See you on atmos. :ringed_planet:
dave.atmos.cv

3 Likes

Hey Dave, great to get a write up of what you’re working on.

I don’t really understand why I’d want to move my account? Running a PDS for new signups is a good pattern that I think many larger apps will do.

I’m (personally) not interested in having public data that’s not in my repo.

I wouldn’t worry too much about seeking consensus first, just make a lexicon that works for you and if it is useful you can converge on something that others apps are compatible with.

3 Likes

Hey Boris. Thanks for the feedback.

The migration flow is mainly there for Bluesky users who want their existing identity and history to travel with them to Atmos, rather than starting fresh. You’re right that for new signups, just provisioning on the Atmos PDS is the cleaner path. We don’t expect most people to migrate but it’s more of a “you can if you want to” escape hatch.

Noted on data in the repo, and honestly that’s part of what’s motivating the lexicon question. Right now portfolio entries and CV records live in Firebase precisely because I hadn’t defined lexicons for them — so they couldn’t go in the repo. Defining cv.atmos.* lexicons would let us write those records to the PDS where they belong, which aligns with what you’re describing.

I’ll write what Atmos needs and post the lexicons publicly. If others find them useful, great; if not, we’ll have learned something.

When you publish the lexicon they should auto populate over here: https://lexicon.garden/

If you want private data currently there is Stratos that does microblogging GitHub - NorthskySocial/stratos: Shared Private Data · GitHub and Bluesky PBC is working on permissioned data spaces too.

PS:
clicking on ur link: dave.atmos.cv opens it as www.dave.atmos.cv which does not exist, just wanted to let you know this happened.

Also after opening ur page it looks to be a linkinbio :slight_smile: I also made a tool linkna.me and you are welcome to compare my lexicon as a starting point me.linkna.linkinbio - Lexicon Garden (linkname frontend is closed-source, but all data is written to the users PDS so the lexicon is open, feel free to copy fork modify anything from the lexicon if anything is of value!)

PSS:
If you want to simplify UX, feel free to fork and copy self.surf ePDS so that you can have Email OTP login, you are of course welcome to use self.surf itself, but then you lose the atmos.cv url which I think you do want to display visibly.