Warning
Alpha: the component, hosted-backend contract, and self-hosted bridge work end-to-end, but the packages and public service have not been announced yet.
Drop-in atmosphere/ATProto comments for any blog, built as SSR-able, hydratable web components with svebcomponents. Readers sign in with their ATProto account and reply from the page; comments live in commenters' own repos on the social web, not in our database.
- ✅ Phase 1 — read-only
<atproto-comments>: fetches and renders a thread (nested replies, tombstones for deleted/blocked posts, moderation-label collapse, depth capping, rich text from facets, root like/repost stats, permalinks to the post). Self-contained bundle ~26 KB gz. - ✅ SSR + hydration: server-rendered declarative shadow DOM is adopted in place on the client — verified by e2e (same-node identity and rich-prop transport), including inside a hydrating SvelteKit host. The live connection may then perform one intentional background sync.
- ✅ Phase 2 — in-page sign-in & posting: set a
serviceand readers sign in with their atmosphere account (OAuth popup + COOP-safe nonce-claim handshake), then reply to the thread or to any comment via a modal composer with a grapheme counter and optimistic append. The hosted bridge ispackages/service-core(framework-agnostic OAuth/posting; ATProto tokens stay server-side), mounted inapps/hostat/atproto. Aviewerprop routes outbound links through any atmosphere viewer (bsky.app default, e.g. deer.social). - ✅ Live comments without polling: the component subscribes to the hosted SSE bridge by default. One multiplexed, filtered Microcosm Spacedust WebSocket serves every active thread in a bridge process. A connection or reply event triggers a coalesced AppView refresh; reconnect is the correctness boundary because Spacedust v0 has no replay cursor.
- 🔜 Phase 3: Standard.site auto-discovery. Also pending before announcing: deploy the bridge to a real domain + a security pass. See the roadmap.
<script type="module" src=".../atproto-comments/dist/client/index.js"></script>
<!-- Hosted OAuth, posting, and live updates are enabled by default. -->
<atproto-comments
thread="https://bsky.app/profile/bsky.app/post/…"
></atproto-comments>
<!-- One property moves all backend features to your own bridge. -->
<atproto-comments
thread="https://bsky.app/profile/bsky.app/post/…"
service="/atproto"
></atproto-comments>With svebcomponents SSR, the component fetches its thread on the server and
serializes threadData for flash-free hydration. The live connection then
acts as the freshness boundary and performs one background sync. Browser-only
consumers fetch immediately, then use the same event-driven refresh path.
- 00-overview.md — executive summary & decisions
- 01-architecture.md — system architecture & SSR design
- 02-component-design.md — component APIs
- 03-oauth-service.md — hosted auth/posting bridge & self-hosting
- 04-roadmap.md — phases & open questions
apps/host documentation site + hosted OAuth/live-events service (SvelteKit)
components/atproto-comments <atproto-comments> web component
packages/atproto-client isomorphic ATProto read utilities + service client
packages/service-core framework-neutral OAuth and posting bridge
configs/* shared eslint/prettier/tsconfig presets
The publishable packages are named @svebcomponents/atproto.comments,
@svebcomponents/atproto.client, and @svebcomponents/atproto.bridge.
Changesets manages their versions and the release workflow publishes them to
npm with provenance after its release PR is merged. See
RELEASING.md for the token-free trusted-publishing setup and
the one-time initial package bootstrap.
pnpm install
pnpm dev # turbo watch dev — showcase at localhost:5173
pnpm build
pnpm test # unit tests (vitest)
# e2e: hydration guarantees against the built adapter-node server
# (requires network access — the showcase renders a live Bluesky thread)
pnpm build && pnpm --filter atproto-host test:e2e