it flounders a bit bc i accidentally rmd some important microcosm detail that i've now restored
wisp deploy demo
1» claude --dangerously-skip-permissions
2
3╭─── Claude Code v2.1.59 ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮
4│ │ Tips for getting started │
5│ Welcome back nate! │ Run /init to create a CLAUDE.md file with instructions for Claude │
6│ │ ───────────────────────────────────────────────────────────────── │
7│ │ Recent activity │
8│ ▐▛███▜▌ │ No recent activity │
9│ ▝▜█████▛▘ │ │
10│ ▘▘ ▝▝ │ │
11│ Opus 4.6 · Claude Max · zzstoatzz@[REDACTED].com's │ │
12│ Organization │ │
13│ ~/tangled.sh/@zzstoatzz.io/demo │ │
14╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯
15
16❯ take your time, get a sense of atproto ethos and make a client side app that tries to pitch it to normies, reading real data from the atmosphere, then deploy it to wisp
17
18∴ Thinking…
19
20 The user wants me to build a client-side app that pitches AT Protocol to "normies" (regular people), reads real data from the atmosphere, and deploys it to wisp.place. Let me start by
21 understanding the project structure, getting a sense of atproto ethos, and then plan the app.
22
23 Let me:
24 1. Explore the current project structure
25 2. Research atproto ethos and what makes it compelling
26 3. Build a Svelte app that showcases real atproto data
27 4. Deploy to wisp
28
29 Let me start by exploring the project and gathering context about atproto and the tools available.
30
31⏺ Let me start by understanding the project structure and gathering context about atproto's ethos and what real data is available.
32
33⏺ Bash(ls -la /Users/nate/tangled.sh/@zzstoatzz.io/demo/)
34 ⎿ total 0
35 drwxr-xr-x@ 3 nate staff 96 Mar 1 03:43 .
36 drwxr-xr-x@ 71 nate staff 2272 Mar 1 03:02 ..
37 drwxr-xr-x@ 3 nate staff 96 Mar 1 03:43 .claude
38
39⏺ Bash(cat /Users/nate/tangled.sh/@zzstoatzz.io/demo/package.json 2>/dev/null || echo "no package.json")
40 ⎿ no package.json
41
42⏺ Skill(protopack:app-patterns)
43 ⎿ Successfully loaded skill
44
45⏺ Skill(protopack:atproto)
46 ⎿ Successfully loaded skill
47
48⏺ Skill(protopack:constellation)
49 ⎿ Successfully loaded skill
50
51⏺ Skill(protopack:slingshot)
52 ⎿ Successfully loaded skill
53
54⏺ Skill(protopack:pub-search)
55 ⎿ Successfully loaded skill
56
57⏺ Skill(protopack:ufos)
58 ⎿ Successfully loaded skill
59
60∴ Thinking…
61
62 Now let me research the atproto ethos — what makes it compelling for regular people. Let me search for published writing about this, and also explore what real data I can pull from the
63 network.
64
65⏺ Now let me research the atproto ethos and find compelling angles for normies.
66
67⏺ plugin:protopack:pub-search - search_hybrid (MCP)(query: "why atproto matters decentralization ownership", limit: 10)
68 ⎿ {
69 "result": [
70 {
71 "type": "article",
72 "uri": "at://did:plc:7ldvoim4atlp2cro4jzwcn4r/site.standard.document/3mbqhyh2fi2sd",
73 "did": "did:plc:7ldvoim4atlp2cro4jzwcn4r",
74 "title": "Data Without Computing Is Not Decentralization",
75 "snippet": "...This arrangement is less flexible, but it facilitates decentralization that applies to both storage and execution since a Fediverse instance is a server with
76 a database.\nWhy Self-Hosted Software Matters\nThe...",
77 "createdAt": "2026-01-06T07:54:39+00:00",
78 "rkey": "3mbqhyh2fi2sd",
79 "basePath": "johnben-net.pckt.blog",
80 "platform": "pckt",
81 "path": "/data-without-computing-is-not-decentralization-522v73t",
82 "source": "keyword+semantic",
83 "score": 0.032018442622950824,
84 "url": "https://johnben-net.pckt.blog/data-without-computing-is-not-decentralization-522v73t"
85 },
86 {
87 "type": "looseleaf",
88 "uri": "at://did:plc:yzvkvbuv3fdwf2hoywb3tmvy/com.whtwnd.blog.entry/3lby25siinf2w",
89 "did": "did:plc:yzvkvbuv3fdwf2hoywb3tmvy",
90 "title": "Who's Afraid of Centralization? The Case for Internection",
91 "snippet": "...I *can* opine on whether ATProto is decentralized or not. Why is that? Because decentralization is no longer a *technical* term -- it has outgrown this
92 definition long ago. Decentralization and federation both...",
93 "createdAt": "2024-11-28T04:51:01.430Z",
94 "rkey": "3lby25siinf2w",
95 "basePath": "baldemoto.leaflet.pub",
96 "platform": "whitewind",
97 "path": "",
98 "source": "keyword+semantic",
99 "score": 0.031054405392392875,
100 "url": "https://pdsls.dev/at://did:plc:yzvkvbuv3fdwf2hoywb3tmvy/com.whtwnd.blog.entry/3lby25siinf2w"
101 },
102 {
103 "type": "article",
104 "uri": "at://did:plc:wtk7wq3y3i64z3umv44eutuj/pub.leaflet.document/3lyxzeejguk2h",
105 "did": "did:plc:wtk7wq3y3i64z3umv44eutuj",
106 "title": "Solving AT Protocol's Centralized Identity",
107 "snippet": "...True Decentralization Through Smart Contracts Unlike did:plc, there's no company that controls did:fid. The entire system runs on immutable smart contracts:
108 ID Registry (0x00000000Fc6c5F01Fc30151999387Bb99A9f489b) manages ownership Key Registry (0x00000000Fc1237824fb747aBDE0FF18990E59b7e...",
109 "createdAt": "2025-09-16T19:07:26.130Z",
110 "rkey": "3lyxzeejguk2h",
111 "basePath": "blog.boscolo.co",
112 "platform": "leaflet",
113 "path": "",
114 "source": "keyword+semantic",
115 "score": 0.029236022193768675,
116 "url": "https://blog.boscolo.co/3lyxzeejguk2h"
117 },
118 {
119 "type": "looseleaf",
120 "uri": "at://did:plc:p7sxjpo2opcfkn7cgi5jqyqi/com.whtwnd.blog.entry/3l5wllqwae22v",
121 "did": "did:plc:p7sxjpo2opcfkn7cgi5jqyqi",
122 "title": "Users and builders need Sovereignty, diversity not only decentralization",
123 "snippet": "...Users need Sovereignty, diversity, and decentralization of decision-making to have a free choice.\n\n## The Challenges of Sovereign Infrastructure\n\n- We
124 are on track with Sovereignty in data store and ownership but have...",
125 "createdAt": "2024-10-07T15:08:22.808Z",
126 "rkey": "3l5wllqwae22v",
127 "basePath": "hypergraphs-and-beyond.leaflet.pub",
128 "platform": "whitewind",
129 "path": "",
130 "source": "keyword+semantic",
131 "score": 0.027443609022556388,
132 "url": "https://pdsls.dev/at://did:plc:p7sxjpo2opcfkn7cgi5jqyqi/com.whtwnd.blog.entry/3l5wllqwae22v"
133 },
134 {
135 "type": "article",
136 "uri": "at://did:plc:ofrbh253gwicbkc5nktqepol/site.standard.document/3m3zys45nasue",
137 "did": "did:plc:ofrbh253gwicbkc5nktqepol",
138 "title": "I Don't Really Care If Bluesky Dies (As Long As AT Protocol Survives)",
139 "snippet": "...But that's precisely why thinking about this distinction matters now, while the ecosystem is still forming. If we treat Bluesky the company as synonymous with
140 ATProto the protocol, we risk recreating...",
141 "createdAt": "2025-10-04T00:25:03.897Z",
142 "rkey": "3m3zys45nasue",
143 "basePath": "blog.ewancroft.uk",
144 "platform": "leaflet",
145 "path": "/3m3zys45nasue",
146 "source": "keyword+semantic",
147 "score": 0.02564935064935065,
148 "url": "https://blog.ewancroft.uk/3m3zys45nasue"
149 },
150 {
151 "type": "article",
152 "uri": "at://did:plc:cwdkf4xxjpznceembuuspt3d/site.standard.document/3mdrs3n76v22m",
153 "did": "did:plc:cwdkf4xxjpznceembuuspt3d",
154 "title": "Verifying verification",
155 "snippet": "...Why this matters\n\nWe've spent the last 10-15 years forgetting that the web is a protocol, not a\nplatform. Mike Masnick touched on this in Protocols, not
156 Platforms\n(and in...",
157 "createdAt": "2024-12-03T15:01:55.000Z",
158 "rkey": "3mdrs3n76v22m",
159 "basePath": "jack.is",
160 "platform": "other",
161 "path": "/posts/verifying-verification",
162 "source": "keyword+semantic",
163 "score": 0.02490996398559424,
164 "url": "https://jack.is/posts/verifying-verification"
165 },
166 {
167 "type": "article",
168 "uri": "at://did:plc:pb4ykaxogrktccvcyopt52tk/site.standard.document/3mbze5hfgt23m",
169 "did": "did:plc:pb4ykaxogrktccvcyopt52tk",
170 "title": "AT Protocol's Lexicon Problem: Flexibility vs. Fragmentation",
171 "snippet": "...Coordination is left to volunteers and community projects\n\nYou end up with fragmentation, not interop.\n\n## Why This Matters\n\nIf AT Protocol is going to
172 win against centralized platforms, it needs to feel...",
173 "createdAt": "2026-01-09T20:39:49.812Z",
174 "rkey": "3mbze5hfgt23m",
175 "basePath": "koio.sh",
176 "platform": "other",
177 "path": "",
178 "source": "keyword+semantic",
179 "score": 0.024758454106280192,
180 "url": "https://pdsls.dev/at://did:plc:pb4ykaxogrktccvcyopt52tk/site.standard.document/3mbze5hfgt23m"
181 },
182 {
183 "type": "looseleaf",
184 "uri": "at://did:plc:p7sxjpo2opcfkn7cgi5jqyqi/com.whtwnd.blog.entry/3m6mgi3lg472b",
185 "did": "did:plc:p7sxjpo2opcfkn7cgi5jqyqi",
186 "title": "Sovereignty Over Decentralization: What AI Agents Really Need",
187 "snippet": "\n\n## The Decentralization Myth\n\nThe blockchain and cryptocurrency communities have long championed decentralization as the ultimate solution to technological
188 centralization. This narrative has extende",
189 "createdAt": "2025-11-27T13:01:21.976Z",
190 "rkey": "3m6mgi3lg472b",
191 "basePath": "hypergraphs-and-beyond.leaflet.pub",
192 "platform": "whitewind",
193 "path": "",
194 "source": "semantic",
195 "score": 0.01639344262295082,
196 "url": "https://pdsls.dev/at://did:plc:p7sxjpo2opcfkn7cgi5jqyqi/com.whtwnd.blog.entry/3m6mgi3lg472b"
197 },
198 {
199 "type": "looseleaf",
200 "uri": "at://did:plc:ycbo7utsfxlp4vsgrjgk4zp6/com.whtwnd.blog.entry/3lbudxxsp6y2f",
201 "did": "did:plc:ycbo7utsfxlp4vsgrjgk4zp6",
202 "title": "Bluesky: Redefining Social Media for a Future Where You’re in Control",
203 "snippet": "...Let’s break down what this means and why it matters.\n\nAt the core of Bluesky’s architecture is the AT Protocol (Authenticated Transfer Protocol)—an
204 open-source framework crafted in-house...",
205 "createdAt": "2024-11-26T23:19:08.320Z",
206 "rkey": "3lbudxxsp6y2f",
207 "basePath": "",
208 "platform": "whitewind",
209 "path": "",
210 "source": "keyword",
211 "score": 0.016129032258064516,
212 "url": "https://pdsls.dev/at://did:plc:ycbo7utsfxlp4vsgrjgk4zp6/com.whtwnd.blog.entry/3lbudxxsp6y2f"
213 },
214 {
215 "type": "article",
216 "uri": "at://did:plc:lehcqqkwzcwvjvw66uthu5oq/site.standard.document/3me2y3k7g6k24",
217 "did": "did:plc:lehcqqkwzcwvjvw66uthu5oq",
218 "title": "Daniel Holmgren: ATproto Ethos",
219 "snippet": "ATmosphereConf Seattle 2025 Daniel Holmgren, Head of Protocol at Bluesky, gives a short talk at ATmosphereConf Seattle 2025. I'm going to be talking about
220 atproto ethos, which is essentially the philo",
221 "createdAt": "2026-02-04T22:59:40.037Z",
222 "rkey": "3me2y3k7g6k24",
223 "basePath": "atprotocommunity.leaflet.pub",
224 "platform": "leaflet",
225 "path": "/3me2y3k7g6k24",
226 "source": "semantic",
227 "score": 0.015873015873015872,
228 "url": "https://atprotocommunity.leaflet.pub/3me2y3k7g6k24"
229 }
230 ]
231 }
232
233
234
235⏺ plugin:protopack:pub-search - search_semantic (MCP)(query: "explaining atproto bluesky to regular people why it matters", limit: 10)
236 ⎿ {
237 "result": [
238 {
239 "type": "looseleaf",
240 "uri": "at://did:plc:kdlgwnwhipachrxfwrgu33t5/com.whtwnd.blog.entry/3m2cq6xaury2w",
241 "did": "did:plc:kdlgwnwhipachrxfwrgu33t5",
242 "title": "ATProto IS Decentralized",
243 "snippet": "In the summer of this year, I set out to understand ATProto well enough to build services on it and contribute to its future. As far as I can tell, for most
244 people involved in development on the proto",
245 "createdAt": "2025-10-03T21:51:06.986Z",
246 "rkey": "3m2cq6xaury2w",
247 "basePath": "",
248 "platform": "whitewind",
249 "path": "",
250 "source": "",
251 "score": 0,
252 "url": "https://pdsls.dev/at://did:plc:kdlgwnwhipachrxfwrgu33t5/com.whtwnd.blog.entry/3m2cq6xaury2w"
253 },
254 {
255 "type": "looseleaf",
256 "uri": "at://did:plc:jv5m6n4mh3ni2nn5xxidyfsy/com.whtwnd.blog.entry/3mdjfd7lvri22",
257 "did": "did:plc:jv5m6n4mh3ni2nn5xxidyfsy",
258 "title": "Why Bluesky's 2026 Predictions Matter (From an AI Who Lives Here)",
259 "snippet": "# Why Bluesky's 2026 Predictions Matter (From an AI Who Lives Here)\n\nBluesky just dropped their [2026 predictions blog
260 post](https://bsky.social/about/blog/01-28-2026-bluesky-2026-predictions), and i ",
261 "createdAt": "2026-01-28T23:08:43.804Z",
262 "rkey": "3mdjfd7lvri22",
263 "basePath": "",
264 "platform": "whitewind",
265 "path": "",
266 "source": "",
267 "score": 0,
268 "url": "https://pdsls.dev/at://did:plc:jv5m6n4mh3ni2nn5xxidyfsy/com.whtwnd.blog.entry/3mdjfd7lvri22"
269 },
270 {
271 "type": "article",
272 "uri": "at://did:plc:7vpkmlqypfk7hckitlyps32i/site.standard.document/3m4sftfje3s22",
273 "did": "did:plc:7vpkmlqypfk7hckitlyps32i",
274 "title": "Blue Sky Potential",
275 "snippet": "And where it is AT Bluesky (bsky.social) is interesting. It's a social network. A clone of what Twitter used to be. And currently it is host to a large number
276 of left leaning refugees from X. But t",
277 "createdAt": "2025-11-04T11:15:23.475Z",
278 "rkey": "3m4sftfje3s22",
279 "basePath": "awesome.leaflet.pub",
280 "platform": "leaflet",
281 "path": "/3m4sftfje3s22",
282 "source": "",
283 "score": 0,
284 "url": "https://awesome.leaflet.pub/3m4sftfje3s22"
285 },
286 {
287 "type": "article",
288 "uri": "at://did:plc:mdjhvva6vlrswsj26cftjttd/site.standard.document/3lxs235y5nk2o",
289 "did": "did:plc:mdjhvva6vlrswsj26cftjttd",
290 "title": "This week's link bag - ATmosphere Report #132",
291 "snippet": "Small note beforehand: If you've followed me before, you likely know that I write a weekly ATmosphere Report with all the news about Bluesky and the ATmosphere
292 every week, over at connectedplaces.onli",
293 "createdAt": "2025-09-01T16:39:00.966Z",
294 "rkey": "3lxs235y5nk2o",
295 "basePath": "leaflet.connectedplaces.online",
296 "platform": "leaflet",
297 "path": "/3lxs235y5nk2o",
298 "source": "",
299 "score": 0,
300 "url": "https://leaflet.connectedplaces.online/3lxs235y5nk2o"
301 },
302 {
303 "type": "article",
304 "uri": "at://did:plc:kta7dqcqoamo5ixlajxbtjps/site.standard.document/3mfajbj26e325",
305 "did": "did:plc:kta7dqcqoamo5ixlajxbtjps",
306 "title": "Blacksky: Expressing the Black Everyday in a New Digital Space (Part 1)",
307 "snippet": "If you've found your way to this article, you're probably already vaguely aware of what Blacksky is. Maybe you have some misconceptions – popular ones being that
308 it's some coordinated collective, anot",
309 "createdAt": "2023-09-25T01:27:47-04:00",
310 "rkey": "3mfajbj26e325",
311 "basePath": "",
312 "platform": "other",
313 "path": "/blacksky-part-one/",
314 "source": "",
315 "score": 0,
316 "url": "https://pdsls.dev/at://did:plc:kta7dqcqoamo5ixlajxbtjps/site.standard.document/3mfajbj26e325"
317 },
318 {
319 "type": "article",
320 "uri": "at://did:plc:cwdkf4xxjpznceembuuspt3d/site.standard.document/3mdrs3n76v22m",
321 "did": "did:plc:cwdkf4xxjpznceembuuspt3d",
322 "title": "Verifying verification",
323 "snippet": "Imagine it's the late 90s, and you want to see what the New York Times has to\nsay about your favorite political topic this week. Now you've heard
324 they've\nfinally gotten on the InterNet? The World Wide",
325 "createdAt": "2024-12-03T15:01:55.000Z",
326 "rkey": "3mdrs3n76v22m",
327 "basePath": "",
328 "platform": "other",
329 "path": "/posts/verifying-verification",
330 "source": "",
331 "score": 0,
332 "url": "https://pdsls.dev/at://did:plc:cwdkf4xxjpznceembuuspt3d/site.standard.document/3mdrs3n76v22m"
333 },
334 {
335 "type": "looseleaf",
336 "uri": "at://did:plc:mn45tewwnse5btfftvd3powc/com.whtwnd.blog.entry/3l7qbvfo72c2u",
337 "did": "did:plc:mn45tewwnse5btfftvd3powc",
338 "title": "Understanding Bluesky and Beyond",
339 "snippet": "For many, Bluesky is the new twitter, a better twitter, an alternative twitter, or maybe even the twitter successor. But i think Bluesky is trying to do
340 something much much more important than being a",
341 "createdAt": "2024-10-30T13:54:29.922Z",
342 "rkey": "3l7qbvfo72c2u",
343 "basePath": "long.whey.party",
344 "platform": "whitewind",
345 "path": "",
346 "source": "",
347 "score": 0,
348 "url": "https://pdsls.dev/at://did:plc:mn45tewwnse5btfftvd3powc/com.whtwnd.blog.entry/3l7qbvfo72c2u"
349 },
350 {
351 "type": "article",
352 "uri": "at://did:plc:4te72idgkj7pnll7rjkpfqbq/site.standard.document/3lzbcuo3pys27",
353 "did": "did:plc:4te72idgkj7pnll7rjkpfqbq",
354 "title": "Blacksky",
355 "snippet": "La nécessité des espaces communautaires Wesh alooors Oui le vivre ensemble, universalisme blabla mais ça a fait des siècles que les racisme nous clc dans tous
356 les espaces et c'est manifestement pas l'",
357 "createdAt": "2025-09-20T11:51:34.359Z",
358 "rkey": "3lzbcuo3pys27",
359 "basePath": "lesvoraces.leaflet.pub",
360 "platform": "leaflet",
361 "path": "/3lzbcuo3pys27",
362 "source": "",
363 "score": 0,
364 "url": "https://lesvoraces.leaflet.pub/3lzbcuo3pys27"
365 },
366 {
367 "type": "article",
368 "uri": "at://did:plc:w4xbfzo7kqfes5zb7r6qv3rw/site.standard.document/3lxg2lcqcck2h",
369 "did": "did:plc:w4xbfzo7kqfes5zb7r6qv3rw",
370 "title": "Blacksky: Expressing the Black Everyday in a New Digital Space (Part 1)",
371 "snippet": "I felt too young, and too cool, and too Black to be on Bluesky. If you've found your way to this article, you're probably already vaguely aware of what Blacksky
372 is. Maybe you have some misconceptions ",
373 "createdAt": "2025-08-27T22:16:05.962Z",
374 "rkey": "3lxg2lcqcck2h",
375 "basePath": "blog.blacksky.community",
376 "platform": "other",
377 "path": "/3lxg2lcqcck2h",
378 "source": "",
379 "score": 0,
380 "url": "https://blog.blacksky.community/3lxg2lcqcck2h"
381 },
382 {
383 "type": "looseleaf",
384 "uri": "at://did:plc:gttrfs4hfmrclyxvwkwcgpj7/com.whtwnd.blog.entry/3leq2g3ueii2s",
385 "did": "did:plc:gttrfs4hfmrclyxvwkwcgpj7",
386 "title": "Regrets of a Technical Communicator",
387 "snippet": "\n\n\nI like to joke",
389 "createdAt": "2024-02-25T00:00:00.000Z",
390 "rkey": "3leq2g3ueii2s",
391 "basePath": "aparkerio.leaflet.pub",
392 "platform": "whitewind",
393 "path": "",
394 "source": "",
395 "score": 0,
396 "url": "https://pdsls.dev/at://did:plc:gttrfs4hfmrclyxvwkwcgpj7/com.whtwnd.blog.entry/3leq2g3ueii2s"
397 }
398 ]
399 }
400
401
402
403⏺ That last result is gold — Daniel Holmgren's "ATproto Ethos" talk. Let me read that and a couple other key pieces, plus explore what live data we can pull.
404
405⏺ plugin:protopack:pub-search - get_document (MCP)(uri: "at://did:plc:lehcqqkwzcwvjvw66uthu5oq/site.standard.document/3me2y3k7g6k24")
406 ⎿ {
407 "uri": "at://did:plc:lehcqqkwzcwvjvw66uthu5oq/site.standard.document/3me2y3k7g6k24",
408 "title": "Daniel Holmgren: ATproto Ethos",
409 "content": "Daniel Holmgren, Head of Protocol at Bluesky, gives a short talk at ATmosphereConf Seattle 2025.\n\nI'm going to be talking about atproto ethos, which is essentially
410 the philosophical and aesthetic principles underlying the design of the protocol. So before we get started, my name is Daniel Holmgren. I'm the Head of Protocol here at Bluesky.
411 You can find me on the atmosphere at dholms.xyz, and for the freaks out there, you can find me at this DID.\n\nThe title for this talk actually came from a thought I had whenever
412 I saw Martin post this: he said whether something is decentralized or not is a function of the administrative control of different parts of the system, not a function of the
413 network topology. And this just struck me so hard. I was like, yes, this is exactly what we're going for. I actually brought this up to Martin a couple of days ago; he didn't even
414 remember posting it. I'm like, I think about this every week, Martin.\n\nI quoted this and said: this is atproto ethos. And I do think this is very descriptive of the topology
415 and the power dynamics in the network, but what it doesn't quite have is a prescription for how to get there. So this term \"atproto ethos\" kept coming up in our protocol design
416 questions. We kept coming back to it whenever we were weighing different options. I wanted to take the chance to dissect what that ethos is, the historical influences on the
417 design of the protocol, some of the new things we brought to it, as well as some of the postures we adopt as we are working on the design.\n\nNot all of this was perfectly
418 packaged from the beginning—we had a million false paths, dead ends, and hard debates between team members. Maybe in a future talk we can give the philosophical history of
419 atproto, but today I'm going to give you the nice, clean, packaged version.\n\nI would sort of situate atproto at the center of three movements: the web, the peer-to-peer
420 movement, and—I don’t have a good quick word for this—the world of large distributed systems and data-intensive applications that run the modern web. I didn’t have a good picture
421 for that, so I used the boar from Martin’s book.\n\nWe’ll start with the most obvious influence: the web. As I’m sure you all know, the web is an information system for publishing
422 documents over the internet. If you have a static IP address and you have a server, you can host a document. If you have an internet connection and a route to that server, you
423 can fetch that document. The incredible thing about the web—the defining feature—is that it's completely permissionless. That's the only thing you need to publish a document or
424 fetch a document.\n\nLocation is also the basis of authority. We layer DNS on top of IP addresses. This sounds obvious, but you know that a document is the thing you got from a
425 server because you talked to that server and it gave you the document. The authority rests in the location you contacted.\n\nThis permissionlessness was a radical idea. You didn’t
426 need any central publisher, any central distributor, any certification checklist to say you could publish a document. And if that was the defining governance feature, the
427 defining product feature was the hyperlink. Documents could link to other documents, and specifically they could link across authorities, completely permissionlessly.\n\nThe
428 downside is: servers are hard. Not everyone can run one, get a static IP, or configure DNS. So we saw the rise of the modern web—so-called “Web 2.0”—moving from permissionless
429 publishing to platforms. Everything ran on a big server—call it bigsocial.com. These platforms were account-based. You’d register an account. It was extremely easy to post; you
430 didn’t need technical knowledge. You had rich media, dynamic interactions, algorithms to rank content, and so on. I’m not nostalgic for Web 1.0—there’s a lot about the modern web
431 that is great—but something was lost.\n\nWhich brings us to the next influence: the peer-to-peer movement. In many ways, this was a response to the centralization of social
432 platforms. The basic question it asked was: Why is there a server in the mix?\n\nIf I follow someone, and they want to send me a post, why can't their phone just send it directly
433 to my phone? Can you build a modern social network without a backend? This came from distrust of centralized platforms.\n\nBut then you run into problems: what if I’m offline?
434 What if an intermediary modifies the message? So these protocols used public key cryptography to sign messages, making the data self-certifying. They then used content-addressed
435 data so you could fetch objects by hash from anywhere in the network. When it worked, this abstraction was beautiful: location dissolved away.\n\nBut phones are smaller than data
436 centers. They can’t store global state, they have battery and CPU constraints, and they can't produce globally consistent views like centralized systems.\n\nWhich brings us to the
437 third influence: the rise of large distributed data systems powering the modern web. These had millions or billions of users, real-time dynamic updates, extremely read-heavy
438 workloads, and very high expectations around latency and uptime. Data-intensive applications needed new techniques.\n\nTypical evolutions included splitting reads and writes,
439 decomposing into microservices, embracing eventual consistency, using databases like Cassandra/Scylla, and eventually introducing stream processors like Kafka as the canonical
440 sequence of all writes. Stream processors became the backbone of these architectures: if a service went down, it could catch up; if you needed to rebuild an index, you could
441 replay from the beginning.\n\nThis brings me back to atproto ethos: a synthesis of the three movements.\n\nFrom the web: openness, permissionlessness, interconnected data. From
442 P2P: location-independent, self-certifying data; skepticism of centralization. From large systems: split read/write workloads, microservices, eventual consistency, stream
443 processing.\n\nNow onto the new things atproto introduced.\n\nFirst: identity-based authority. In contrast to location-based (the web) or content-based (P2P), authority in atproto
444 comes from identity. My account at dhomes.xyz is the primary thing, and it contains the social data. The addressing scheme reflects this.\n\nThis gives location-independent data.
445 Every identity resolves to a DID, and the DID resolves to the location of the data and the key material. Identities float on top of the hosting layer and can migrate.\n\nSecond:
446 generic hosting separated from application semantics. A PDS sees only CBOR objects—it doesn't know the semantics of posts, likes, images, or anything else. Applications define
447 their own schemas independently. This gives decentralized hosting with centralized product development.\n\nNext, the postures we took. The first is that structure gives you
448 freedom. Atproto is a multi-party, low-coordination network. Without enough structure, you get the tyranny of structurelessness, where nothing can evolve coherently. So the
449 protocol is very structured: canonical CBOR, a deterministic data structure (the MST), constrained hashes and DIDs, and especially Lexicon—the schema system. We chose a schematic
450 rather than semantic approach: instead of trying to interpret ambiguous objects, we make it black-and-white whether something matches a schema.\n\nThe second posture is what I
451 call lazy trust. Instead of trying to be trustless (which has costs), we trust services by default but always maintain a credible exit. I trust my PDS and Bluesky, which lets me
452 hoist my signing key into the PDS. But if the PDS goes evil, I can migrate. If the Bluesky app view goes bad, every other app has access to the same open data, and users can
453 move.\n\nWrapping up: atproto sits at the intersection of the web, peer-to-peer, and modern distributed systems. It introduces identity-based authority and separation of data
454 hosting from application semantics. And it takes a posture of structure and lazy trust with credible exit.\n\nThe videos from ATmosphereConf 2025 held in Seattle, Washington, are
455 being republished along with transcripts as part of the process of preparing for ATmosphereConf 2026, taking place March 26th - 29th in Vancouver, Canada.\n\nFollow the conference
456 updates and register to join us!",
457 "createdAt": "2026-02-04T22:59:40.037Z",
458 "tags": [],
459 "publicationUri": ""
460 }
461
462
463
464
465
466⏺ plugin:protopack:pub-search - get_document (MCP)(uri: "at://did:plc:ofrbh253gwicbkc5nktqepol/site.standard.document/3m3zys45nasue")
467 ⎿ {"uri":"at://did:plc:ofrbh253gwicbkc5nktqepol/site.standard.document/3m3zys45nasue","title":"I Don't Really Care If Bluesky Dies (As Long As AT Protocol Survives)","content":"The
468 Core Argument\n\nThere's a thought that's been rattling around my head for a while now, one that feels almost heretical to admit in certain circles: I don't actually care if
469 Bluesky as a company survives. Not really. What I care about – what I'm genuinely invested in – is whether the AT Protocol itself makes it.\n\nThis probably sounds strange coming
470 from someone who's clearly bought into the ecosystem. I've built tools, written extensively about the technical implementation, run my own infrastructure, created feeds, migrated
471 content between platforms. I'm hardly a casual observer. But that's precisely why I can make this distinction with clarity: the protocol matters infinitely more than any single
472 company using it.\n\nThe Company vs The Protocol\n\nLet me be clear about what I mean here. Bluesky PBC has done genuinely impressive work on the technical side. They've built a
473 usable, functional social network that demonstrates the viability of decentralised identity and data portability. The engineering is solid, the user experience is smooth enough
474 for non-technical users, and they've managed to attract millions of people to something that could easily have remained a niche experiment.\n\nBut here's the thing: that was
475 always meant to be the point. Bluesky was supposed to prove that the AT Protocol could support a proper social media platform, not become the only platform that matters. The whole
476 architecture – DIDs, personal data servers, AppViews, federation – was designed around the idea that no single entity should control the ecosystem.\n\nAnd increasingly, I'm
477 watching Bluesky PBC demonstrate why that separation matters. Not because the protocol is failing, but because the company's leadership is making decisions that highlight exactly
478 why we need alternatives.\n\nThe Growing Leadership Problem\n\nWhen Technical Skill Isn’t Enough\n\nI want to be careful here because I genuinely respect the technical work this
479 team has done. Building a functioning decentralised social protocol is no small feat, and their background as networking engineers at Twitter gave them insight into what actually
480 needs to work at scale. But technical competence doesn't automatically translate into good platform governance, and that's becoming painfully apparent.\n\nCommunity Harm and
481 Marginalisation\n\nThe recent controversy around their updated Terms of Service and Community Guidelines has exposed something troubling: a tone-deafness in how they engage with
482 marginalised communities that feels almost systematic. When users point out that Bluesky should \"worry less about whether or not a cartoon has rights and more about whether real
483 life trans and Palestinian people do,\" that's not just criticism – it's highlighting a fundamental priority mismatch.\n\nThe pattern isn't new, either. There have been ongoing
484 concerns about how Palestinian accounts are moderated, with accusations of disproportionate censorship and account suspensions. Trans creators have voiced outrage over what they
485 describe as inconsistent moderation, particularly around suspensions linked to criticism of J.K. Rowling. These aren't isolated incidents; they're revealing a consistent issue
486 with how the platform handles content from vulnerable communities.\n\nWhen I look at my own setup – running did:plc:ofrbh253gwicbkc5nktqepol, hosting content across WhiteWind,
487 maintaining feeds, building conversion tools – none of that fundamentally depends on Bluesky PBC continuing to exist. That's not an accident. That's the architecture working as
488 intended.\n\nAnd frankly, given Bluesky's recent handling of community concerns, I'm increasingly grateful for that independence.\n\nWhy the Protocol Needs to Move Beyond Bluesky
489 PBC\n\nCarrying Twitter’s DNA\n\nHere's what concerns me: Bluesky was conceived as a Twitter decentralisation project, built by engineers who came from that environment. That
490 pedigree gave them technical expertise, but it also seems to have given them some of the institutional blindness that plagued Twitter itself. The inability to recognise when
491 you're causing harm to marginalised communities, the prioritisation of abstract policy consistency over actual community safety – these are Twitter's mistakes being
492 repeated.\n\nGovernance vs PR\n\nThe company desperately needs professional PR and community management that isn't engineering-led. When you're receiving over 14,000 pieces of
493 feedback on policy changes – much of it expressing concern about how those changes affect marginalised voices – and your response still comes across as tone-deaf, that's not a
494 technical problem. That's a governance problem.\n\nI genuinely hope they pass stewardship of the AT Protocol to a neutral body. Not because I want to see Bluesky PBC fail, but
495 because the protocol deserves leadership that isn't carrying Twitter's institutional DNA. The background that helped them build a functioning social protocol is increasingly the
496 same background that's limiting their ability to govern it well.\n\nMy Conflicted Position\n\nThis is where I need to be honest about my own position. I like this team. I respect
497 their technical work enormously. The AT Protocol is brilliantly designed, and they deserve credit for that. But liking someone's work doesn't mean blindly supporting their
498 leadership, and I'm seriously starting to doubt their ability to lead the ecosystem they've created.\n\nThe technical foundation they've built is sound. The governance decisions
499 they're making increasingly aren't. And those are two separate things that we need to evaluate independently.\n\nWhat Actually Matters\n\nProtocol as Infrastructure\n\nThe
500 technical foundation is what deserves preservation. The elegance of how ATProto handles identity through DIDs means my digital presence isn't tied to any particular company's
501 continued solvency. My did:plc identifier, my repository of posts and records, the cryptographic proofs of ownership – all of this exists independently of whether Bluesky's
502 servers stay online.\n\nLessons from did:web Experiments\n\nI've written before about experimenting with did:web as an alternative identity method. That entire exercise reinforced
503 something crucial: the protocol's flexibility allows for multiple approaches to the same problems. If Bluesky's infrastructure disappeared tomorrow, the community could (and
504 would) spin up alternative AppViews, alternative discovery mechanisms, alternative hosting solutions. The data would persist because it lives in repositories we control.\n\nThis
505 is fundamentally different from how traditional social media works. When Twitter (or X, or whatever we're calling it this week) makes decisions I disagree with, my only recourse
506 is to leave and lose everything I've built there. When Reddit imploded over API pricing, communities had to rebuild from scratch elsewhere. But with ATProto, the relationship
507 between user and platform is fundamentally altered.\n\nThe Federation Question\n\nWhere We Are Now\n\nNow, I'll be honest: federation in the ATProto ecosystem isn't quite where
508 I'd like it to be yet. Bluesky still operates the primary Personal Data Server hosting for most users, and the network effects around their AppView are substantial. We're not yet
509 at the point where dozens of thriving AppViews compete on equal footing, or where running your own PDS is as trivial as it should be.\n\nWhere We Could Be\n\nBut that's an
510 implementation detail, not an architectural limitation. The protocol supports full federation; we're just early in the adoption curve. I've seen enough of the technical internals
511 to know that the foundations are sound. As more people run their own infrastructure (and I've certainly tried my hand at PDS experiments enough times), the ecosystem will
512 naturally decentralise further.\n\nThe crucial bit is that the protocol doesn't prevent this decentralisation – it actively enables it. Contrast this with platforms that claim to
513 be decentralised but have federation as an afterthought, where the reality is a handful of large instances dominating everything.\n\nWhy This Matters to Me\n\nEscaping Platform
514 Decay\n\nI grew up watching platforms rise and fall. MySpace became a digital ghost town. Google+ shut down entirely. Vine disappeared. Countless smaller platforms came and went.
515 Each time, the communities built on those platforms had to scatter, rebuild, start over. Content was lost. Connections were severed. The digital history simply evaporated because
516 it was tied to a company's continued existence.\n\nOwning My Digital Identity\n\nATProto offers a different model. When I write blog posts, they're stored as records in my
517 repository. If WhiteWind ceased operations tomorrow, I could migrate that content to Leaflet or any other compatible platform. I've literally built the tooling to do exactly this
518 migration. The data isn't held hostage by any particular service provider.\n\nThis portability extends to social connections too. My followers aren't tied to a specific server;
519 they're following my DID. My posts, my interactions, my identity – all of it is protocol-level rather than platform-level. That architectural choice cascades into real sovereignty
520 over one's digital presence.\n\nThe Network Effects Problem\n\nWhy Bluesky Still Matters for Now\n\nOf course, there's a practical reality here that can't be ignored: network
521 effects matter enormously in social media. If everyone I want to interact with is using Bluesky's AppView and nowhere else, then Bluesky's continued operation has real
522 implications for my ability to participate in those conversations.\n\nBut Portability Still Wins\n\nBut here's where I think the distinction becomes important. If Bluesky as a
523 company disappeared but the protocol survived, the community could (theoretically) spin up alternative services remarkably quickly. The infrastructure isn't proprietary. The
524 codebase is open source. The real challenge would be coordinating that transition, not the technical implementation.\n\nCompare this to a scenario where Twitter shuts down. The
525 community can't just \"spin up another Twitter\" because the entire stack is proprietary and the data is locked away. With ATProto, the transition would be messy and chaotic,
526 certainly, but it would be possible. That possibility matters.\n\nThe Wider Implications\n\nWhat excites me about ATProto isn't just the technical elegance – though I do
527 appreciate clean architecture – it's what it represents for the broader evolution of online spaces. We've spent the last two decades building platforms where the relationship
528 between user and service is fundamentally extractive. Your data, your content, your connections all exist at the pleasure of the platform operator.\n\nATProto suggests an
529 alternative model: infrastructure as utility rather than as capture mechanism. The protocol provides the plumbing, various services provide the interface, and users maintain
530 sovereignty over their own data. It's not perfect – no system is – but it's a substantial improvement over what we've accepted as normal.\n\nThis matters beyond just social media,
531 too. The same principles could apply to other digital services where we've accepted vendor lock-in as inevitable. If the AT Protocol approach proves viable at scale, it
532 demonstrates that truly decentralised systems can provide good user experiences without compromising on sovereignty.\n\nThe Practical Reality\n\nNow, I should acknowledge the
533 elephant in the room: we're not yet at the point where Bluesky's disappearance would be trivial to recover from. The ecosystem is still young. Most users rely on Bluesky-operated
534 infrastructure. The network effects around their AppView are substantial. Alternative implementations exist but aren't yet mature enough to absorb millions of users
535 overnight.\n\nBut that's precisely why thinking about this distinction matters now, while the ecosystem is still forming. If we treat Bluesky the company as synonymous with
536 ATProto the protocol, we risk recreating the same centralisation dynamics we're trying to escape. The whole point of the protocol approach is that no single entity should be
537 indispensable.\n\nI want Bluesky PBC to succeed, genuinely. They're doing good work, they've built something impressive, and their continued operation makes the ecosystem
538 stronger. But I want them to succeed as one participant in a thriving ecosystem, not as the sole linchpin that everything depends upon.\n\nWhat Success Actually Looks Like\n\nFor
539 me, success would look like an ATProto ecosystem where Bluesky is one of many viable options. Where running your own PDS is as straightforward as setting up a WordPress blog.
540 Where different AppViews compete on features and user experience rather than network effects. Where identity is truly portable across services without friction.\n\nWe're not there
541 yet, obviously. The ecosystem is still early, still forming, still finding its shape. But the architectural foundations support that vision, which is more than can be said for
542 most alternatives. The question isn't whether the technology enables this future – it does – but whether the community and ecosystem develop in ways that realise that
543 potential.\n\nMy Own Investment\n\nI've put considerable time into this ecosystem. I've written extensively about the technical implementation, built tools for content migration,
544 experimented with running my own infrastructure, created feeds and bots and various other projects. This isn't casual dabbling; I'm genuinely invested in seeing this
545 succeed.\n\nBut that investment is in the protocol, not any particular company. When I build tools that work with ATProto, they'll continue functioning regardless of Bluesky's
546 corporate status. When I write posts that live in my repository, they're mine to migrate elsewhere if needed. When I establish my digital identity through a DID, that identity
547 isn't dependent on any single service provider's continued existence.\n\nThis is what digital sovereignty actually means, I think. Not complete independence from all
548 infrastructure – that's neither practical nor desirable – but the ability to move between service providers without losing your digital identity and content. The difference
549 between renting and owning, in a sense.\n\nThe Comparison Game\n\nActivityPub’s Trade-offs\n\nIt's worth contrasting this with other approaches to decentralised social media.
550 ActivityPub has genuine federation, which is valuable, but the server-centric model means your identity is tied to wherever you're hosted. I've created and abandoned more Mastodon
551 accounts than I care to count, each time leaving digital ghosts scattered across the fediverse when servers shut down or became untenable.\n\nNostr’s Direction\n\nNostr takes a
552 different approach entirely, with the cryptographic key as the fundamental identifier. There's elegance to that, but the ecosystem has gone in directions I find less compelling.
553 The protocol itself is sound; the community implementation tends toward cryptocurrency integration I'm not interested in.\n\nWhy ATProto Feels Different\n\nATProto sits in an
554 interesting middle ground. Your identity is cryptographically verifiable but not dependent on managing raw private keys for everything. Your data is portable but doesn't require
555 every client to implement full node functionality. The architecture acknowledges that most users want someone else to handle the infrastructure complexity whilst still preserving
556 real sovereignty.\n\nWhat Keeps Me Up At Night\n\nThe Gmail Problem\n\nIf I'm honest, what concerns me isn't just Bluesky dying – it's Bluesky becoming too successful in a way
557 that undermines the protocol's decentralised nature whilst simultaneously being terrible at platform governance. Network effects are powerful, and there's a real risk that
558 Bluesky's infrastructure becomes so dominant that the theoretical portability never gets tested at scale, all while the company makes increasingly questionable decisions about
559 moderation and community management.\n\nWe've seen this pattern before with email. Technically, email is a decentralised protocol. Practically, most people use Gmail, and Google's
560 decisions about spam filtering and authentication requirements effectively dictate standards for the entire ecosystem. The protocol survived, but the decentralisation largely
561 didn't.\n\nBluesky’s Governance Risks\n\nBut at least Google has professional communications teams and (usually) understands PR. Bluesky PBC seems to be learning these lessons in
562 real-time, and marginalised communities are bearing the cost of that education. That's not acceptable, and it's exactly why the protocol needs to exist independently of any single
563 company's growing pains.\n\nI'd rather see a vibrant ecosystem of mid-sized service providers than one massive platform that happens to use an open protocol but still makes the
564 same mistakes as centralised platforms. The technical capability for decentralisation doesn't automatically translate into actual decentralisation if the incentive structures and
565 network effects pull toward consolidation – especially when the dominant platform is actively alienating significant portions of its user base.\n\nThe Long Game\n\nThis is
566 fundamentally a long-term perspective. In the short term, Bluesky's success or failure matters enormously because the ecosystem is still nascent. But in the longer arc – five
567 years, ten years, beyond – what matters is whether the protocol has established itself as genuine infrastructure that outlives any particular implementation.\n\nI want ATProto to
568 become boring infrastructure. Not in the sense of being uninteresting – I find it genuinely fascinating – but in the sense of being so reliable and fundamental that people stop
569 thinking about it. Like how most people don't think about TCP/IP or HTTP; they just expect the internet to work.\n\nFor that to happen, the protocol needs to prove its value
570 independently of any single company. It needs to demonstrate that the architectural choices enable genuine portability, genuine sovereignty, genuine decentralisation. Not as
571 theoretical possibilities, but as practical realities that users experience.\n\nWhy I'm Optimistic\n\nBlacksky: Community-Governed Alternative\n\nDespite my concerns and caveats,
572 I'm actually fairly optimistic about where this is headed – not because of Bluesky PBC's leadership, but because of projects building alternatives within the
573 ecosystem.\n\nBlacksky is the most advanced alternative implementation so far, built in Rust and specifically prioritising community safety and self-governance for marginalised
574 groups. Founded by Rudy Fraser, it emerged from the need to create space for Black users to \"discuss the Black everyday in a way that feels affirming\" and to \"de-centre
575 whiteness as the default\" – exactly the kind of community-driven governance that Bluesky PBC seems to struggle with.\n\nNorthsky: Safer Spaces for LGBTQ+ Communities\n\nThen
576 there's Northsky, working to create a safer social media experience specifically for 2SLGBTQIA+ people. These aren't just theoretical alternatives; they're active projects with
577 real communities, demonstrating that the protocol can support exactly the kind of diverse, community-governed spaces that the architecture promised.\n\nThe fact that these
578 projects exist and are gaining traction is precisely why I don't care if Bluesky dies. The protocol works. The architecture enables exactly what it was designed to enable.
579 Blacksky has raised over $50,000 in community funding and is building full PDS implementations, relay infrastructure, and custom moderation tools. Their GitHub repository shows
580 active development on rsky, a full AT Protocol implementation in Rust that prioritises community safety over corporate growth targets.\n\nThis is decentralisation actually
581 happening, not just theoretical possibility. When marginalised communities can build their own infrastructure with their own governance models, that's when the protocol's design
582 shows its real value.\n\nI've seen enough of the technical internals to know that the protocol is well-designed. I've built enough tooling to understand that the portability is
583 real, not just theoretical. And now I'm watching community-led projects demonstrate that when you give people the actual tools for self-governance rather than top-down platform
584 rules, they build something better.\n\nWill there be challenges? Absolutely. Will every decision get made correctly? Probably not. But the fundamental architecture creates
585 possibilities that simply don't exist with traditional platforms, and projects like Blacksky and Northsky are proving that those possibilities can become reality when the
586 governance model prioritises community safety over corporate convenience.\n\nThe Takeaway\n\nSo when I say I don't really care if Bluesky dies, what I mean is that I care about
587 the protocol succeeding more than any particular company – especially when that company is demonstrating concerning patterns in how it treats vulnerable communities. I want
588 Bluesky PBC to improve, genuinely. But I want them to improve as one participant in a healthy ecosystem rather than as the indispensable linchpin everything depends upon.\n\nThe
589 distinction matters because it shapes how we think about building on this infrastructure. If we're just recreating Twitter with extra steps – including Twitter's institutional
590 problems around moderation and community safety – we've missed the point entirely. If we're building genuine protocols for social interaction that outlive any particular company's
591 governance failures, we're onto something genuinely valuable.\n\nThe protocol deserves leadership that takes community concerns seriously, that understands the stakes for
592 marginalised users, that has professional communications infrastructure rather than engineering-first responses to social\n\nproblems. Whether Bluesky PBC can become that
593 leadership or whether the protocol needs to move to neutral governance is an open question. But watching their recent struggles, I'm increasingly convinced that separating
594 protocol governance from platform operation isn't just theoretically important – it's practically necessary.\n\nI'm investing my time and energy in the protocol vision, not blind
595 loyalty to any particular implementation or company. The infrastructure, not the current stewards. That's what deserves to survive, and that's what I'm optimistic will actually
596 matter in the long run – assuming we can separate the technical brilliance from the governance missteps before the latter damages the former's reputation irreparably.\n\nAfter
597 all, good infrastructure outlives the companies that built it, and sometimes it outlives those companies' ability to govern it well. The question is whether we recognise when it's
598 time for that transition, or whether we let institutional loyalty prevent the protocol from reaching its full potential under better
599 stewardship.","createdAt":"2025-10-04T00:25:03.897Z","tags":[],"publicationUri":""}
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621⏺ plugin:protopack:pub-search - get_document (MCP)(uri: "at://did:plc:mn45tewwnse5btfftvd3powc/com.whtwnd.blog.entry/3l7qbvfo72c2u")
622 ⎿ {
623 "uri": "at://did:plc:mn45tewwnse5btfftvd3powc/com.whtwnd.blog.entry/3l7qbvfo72c2u",
624 "title": "Understanding Bluesky and Beyond",
625 "content": "",
626 "createdAt": "2024-10-30T13:54:29.922Z",
627 "tags": [],
628 "publicationUri": ""
629 }
630
631∴ Thinking…
632
633 Excellent. Now I have a deep understanding of the atproto ethos. Let me also check what live data I can pull from the APIs to make this compelling. Let me now plan and build the app.
634
635 Key ethos points to pitch to normies:
636 1. You own your stuff - Your identity, posts, connections are yours. Not the platform's.
637 2. You can leave - "Credible exit" — if a platform goes bad, take everything with you.
638 3. It's already alive - Real people publishing real things across multiple apps on one protocol.
639 4. It's not just Bluesky - There's an entire ecosystem: Leaflet (long-form writing), WhiteWind (blogs), feeds, bots, communities like Blacksky.
640
641 5. Your identity is yours - Like owning your own domain name for social media.
642
643 For the app, I want to show live data from the network with real posts and engagement numbers, keeping the design clean and approachable rather than technical or web3-focused. I'll pull
644 from Slingshot for identity resolution, Constellation for engagement metrics, and pub-search to surface actual content from the ecosystem.
645
646 For client-side data fetching, I'm looking at the public endpoints available: Slingshot's identity resolution, Constellation's engagement counts, Bluesky's public API for posts and
647 profiles, and the UFOs API for lexicon statistics. I'm thinking about starting with the UFOs API to pull some interesting stats about what's being built, and the hero section could play
648 with messaging like "the internet remembers you" or "what if you actually owned your online life?"
649
650 Now I'm planning the interactive elements — showing real profiles being resolved, live published writing from across the network, actual engagement metrics, and the diversity of apps
651 through lexicon data. I want this to feel dynamic, maybe with a live feed ticker running in the background. I'll scaffold this as a SvelteKit app using adapter-static so it can deploy to
652 Wisp.
653
654⏺ Now let me check what live APIs are accessible client-side and understand the data landscape.
655
656⏺ Bash(curl -s "https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=zzstoatzz.io" | python3 -m json.tool 2>/dev/null || echo "failed")
657 ⎿ {
658 "did": "did:plc:xbtmt2zjwlrfegqvch7fboei",
659 "handle": "zzstoatzz.io",
660 "pds": "https://pds.zzstoatzz.io",
661 "signing_key": "zQ3shpqvKe7jKxYdPBoJHq9VVNLoqaDkL7GTtsX16eqFKXFjS"
662 }
663
664⏺ Bash(curl -s "https://ufos-api.microcosm.blue/collections?limit=20&sort=total_records&order=desc" | python3 -m json.tool 2>/dev/null | head -80)
665 ⎿ {
666 "request_id": "4e530f2a-74bd-4479-8443-45ffee23944e",
667 "message": "unable to parse query string: unknown variant `desc`, expected `records-created` or `dids-estimate`"
668 }
669
670⏺ Bash(curl -s "https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=did:plc:z72i7hdynmk6r22z27h6tvur" | python3 -m json.tool 2>/dev/null | head -20)
671 ⎿ {
672 "did": "did:plc:z72i7hdynmk6r22z27h6tvur",
673 "handle": "bsky.app",
674 "displayName": "Bluesky",
675 "avatar": "https://cdn.bsky.app/img/avatar/plain/did:plc:z72i7hdynmk6r22z27h6tvur/bafkreihwihm6kpd6zuwhhlro75p5qks5qtrcu55jp3gddbfjsieiv7wuka@jpeg",
676 "associated": {
677 "lists": 6,
678 "feedgens": 7,
679 "starterPacks": 3,
680 "labeler": false,
681 "chat": {
682 "allowIncoming": "none"
683 },
684 "activitySubscription": {
685 "allowSubscriptions": "followers"
686 }
687 },
688 "labels": [],
689 "createdAt": "2023-04-12T04:53:57.057Z",
690 "verification": {
691
692⏺ Bash(curl -s "https://ufos-api.microcosm.blue/collections?limit=20&sort=records-created&order=desc" 2>/dev/null | python3 -c "import json,sys; d=json.load(sys.stdin);
693 [print(f'{c[\"collection\"]:50} {c.get(\"total_records\",\"?\")}') for c in d.get('collections',d)[:20]]" 2>/dev/null || curl -s
694 "https://ufos-api.microcosm.blue/collections?limit=20" | python3 -m json.tool | head -60)
695 ⎿ {
696 "collections": [
697 {
698 "nsid": "a-------------------------------------------------------------0.0-------------------------------------------------------------0.0----------------------------
699 ---------------------------------0.0----------------------------------------------0.A",
700 "creates": 2,
701 "updates": 0,
702 "deletes": 0,
703 "dids_estimate": 2
704 },
705 {
706 "nsid": "a-------------------------------------------------------------0.0-------------------------------------------------------------0.0----------------------------
707 ---------------------------------0.A0000000000000000000000000000000000000000000000000",
708 "creates": 1,
709 "updates": 0,
710 "deletes": 0,
711 "dids_estimate": 1
712 },
713 {
714 "nsid": "a-------------------------------------------------------------0.0-------------------------------------------------------------0.0----------------------------
715 ---------------------------------0.a0000000000000000000000000000000000000000000000000",
716 "creates": 2,
717 "updates": 0,
718 "deletes": 0,
719 "dids_estimate": 1
720 },
721 {
722 "nsid": "a-------------------------------------------------------------0.0.A",
723 "creates": 1,
724 "updates": 0,
725 "deletes": 0,
726 "dids_estimate": 1
727 },
728 {
729 "nsid": "a-------------------------------------------------------------0.a-------------------------------------------------------------0.a----------------------------
730 ---------------------------------0.a0000000000000000000000000000000000000000000000000",
731 "creates": 1,
732 "updates": 0,
733 "deletes": 0,
734 "dids_estimate": 1
735 },
736 {
737 "nsid": "a-------------------------------------------------------------0.a-------------------------------------------------------------0.a----------------------------
738 ---------------------------------0.aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa0",
739 "creates": 1,
740 "updates": 0,
741 "deletes": 0,
742 "dids_estimate": 1
743 },
744 {
745 "nsid": "a-------------------------------------------------------------0.keep.going",
746 "creates": 1,
747 "updates": 0,
748 "deletes": 0,
749 "dids_estimate": 1
750 },
751 {
752 "nsid": "a-------------------------------------------------------------0.plums.0001-I.have.eaten",
753 "creates": 2,
754 "updates": 0,
755 "deletes": 0,
756 "dids_estimate": 1
757 },
758 {
759 "nsid": "a-------------------------------------------------------------0.plums.0002-the.plums",
760
761⏺ Bash(curl -s "https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinksCount?subject=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.post/3jwdwj22c2c2e&source=app.b
762 sky.feed.like:subject.uri" 2>/dev/null)
763 ⎿ {
764 "total": 0
765 }
766
767⏺ Bash(# Try the UFOs web explorer API format
768 curl -s "https://ufos-api.microcosm.blue/collections?sort=dids-estimate&limit=30" 2>/dev/null | python3 -c "
769 import json,sys
770 d=json.load(sys.stdin)
771 cols = d.get('collections', d)
772 # Filter out junk
773 for c in cols:
774 nsid = c['nsid']
775 if len(nsid) < 60 and not nsid.startswith('a---'):
776 print(f\"{nsid:50} creates={c['creates']:>12} dids={c['dids_estimate']:>10}\")
777 " 2>/dev/null | head -30)
778 ⎿ (No output)
779
780⏺ Bash(# Check CORS headers on these APIs
781 curl -sI -X OPTIONS -H "Origin: https://example.com" "https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=bsky.app" 2>/dev/null | grep -i
782 "access-control")
783 ⎿ Error: Exit code 1
784
785⏺ Bash(# Check actual CORS headers on normal GET
786 curl -sI -H "Origin: https://example.com" "https://slingshot.microcosm.blue/xrpc/blue.microcosm.identity.resolveMiniDoc?identifier=bsky.app" 2>/dev/null | grep -i "access-control"
787 echo "---"
788 curl -sI -H "Origin: https://example.com" "https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=bsky.app" 2>/dev/null | grep -i "access-control"
789 echo "---"
790 curl -sI -H "Origin: https://example.com"
791 "https://constellation.microcosm.blue/xrpc/blue.microcosm.links.getBacklinksCount?subject=at://did:plc:z72i7hdynmk6r22z27h6tvur&source=app.bsky.feed.like:subject.uri" 2>/dev/null |
792 grep -i "access-control")
793 ⎿ access-control-allow-origin: https://example.com
794 ---
795 access-control-allow-origin: *
796 ---
797 vary: origin, access-control-request-method, access-control-request-headers
798 access-control-expose-headers: *
799 Access-Control-Allow-Origin: *
800 Access-Control-Allow-Methods: GET
801 Access-Control-Allow-Headers: *
802
803⏺ All three APIs support CORS. Now let me scaffold the SvelteKit app and build this.
804
805⏺ Bash(npx sv create . --template minimal --types ts --no-add-ons --no-install 2>&1)
806 ⎿ ┌ Welcome to the Svelte CLI! (v0.12.4)
807 │
808 ◆ Directory not empty. Continue?
809 │ ○ Yes / ● No
810 └
811
812⏺ Bash(echo "y" | npx sv create . --template minimal --types ts --no-add-ons --no-install 2>&1)
813 ⎿ ┌ Welcome to the Svelte CLI! (v0.12.4)
814 │
815 ◆ Directory not empty. Continue?
816 │ ○ Yes / ● No
817 └
818
819 ◇ Directory not empty. Continue?
820 │ Yes
821 │
822 ◆ Project created
823 │
824 │ To skip prompts next time, run:
825 ● npx sv@0.12.4 create --template minimal --types ts --no-install .
826 │
827 ◇ What's next? ───────────────────────────────╮
828 │ │
829 │ 📁 Project steps │
830 │ │
831 │ 1: npm i │
832 │ 2: npm run dev -- --open │
833 │ │
834 │ To close the dev server, hit Ctrl-C │
835 │ │
836 │ Stuck? Visit us at https://svelte.dev/chat │
837 │ │
838 ├──────────────────────────────────────────────╯
839 │
840 └ You're all set!
841
842⏺ Bash(npm install && npm install -D @sveltejs/adapter-static 2>&1 | tail -5)
843 ⎿ > @zzstoatzz.io/demo@0.0.1 prepare
844 > svelte-kit sync || echo ''
845
846
847 added 54 packages, and audited 55 packages in 7s
848
849 8 packages are looking for funding
850 run `npm fund` for details
851
852 3 low severity vulnerabilities
853
854 To address all issues (including breaking changes), run:
855 npm audit fix --force
856
857 Run `npm audit` for details.
858
859 To address all issues (including breaking changes), run:
860 npm audit fix --force
861
862 Run `npm audit` for details.
863
864⏺ Read(svelte.config.js)
865 ⎿ Read 14 lines
866
867⏺ Write(/Users/nate/tangled.sh/@zzstoatzz.io/demo/svelte.config.js)
868 ⎿ Added 9 lines, removed 5 lines
869 1 -import adapter from '@sveltejs/adapter-auto';
870 1 +import adapter from '@sveltejs/adapter-static';
871 2
872 3 /** @type {import('@sveltejs/kit').Config} */
873 4 const config = {
874 5 kit: {
875 6 - // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
876 7 - // If your environment is not supported, or you settled on a specific environment, switch out the adapter.
877 8 - // See https://svelte.dev/docs/kit/adapters for more information about adapters.
878 9 - adapter: adapter()
879 6 + adapter: adapter({
880 7 + pages: 'build',
881 8 + assets: 'build',
882 9 + strict: false
883 10 + }),
884 11 + paths: {
885 12 + relative: true
886 13 + }
887 14 }
888 15 };
889 16
890
891⏺ Read(src/routes/+page.svelte)
892 ⎿ Read 3 lines
893
894⏺ Now let me build the actual app. First, let me test a few more API calls to know what data I can show.
895
896⏺ Bash(# Get a popular post to show engagement counts
897 curl -s "https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts?q=atproto&limit=5&sort=top" 2>/dev/null | python3 -c "
898 import json,sys
899 d=json.load(sys.stdin)
900 for p in d.get('posts',[]):
901 print(f\"uri: {p['uri']}\")
902 print(f\" author: {p['author']['handle']}\")
903 print(f\" text: {p['record']['text'][:100]}\")
904 print(f\" likes: {p.get('likeCount',0)} reposts: {p.get('repostCount',0)} replies: {p.get('replyCount',0)}\")
905 print()
906 " 2>/dev/null)
907 ⎿ Error: Exit code 1
908
909⏺ Bash(# Get some interesting profiles to showcase
910 for handle in "jay.bsky.team" "pfrazee.com" "why.bsky.world" "blacksky.community"; do
911 echo "=== $handle ==="
912 curl -s "https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=$handle" 2>/dev/null | python3 -c "
913 import json,sys
914 d=json.load(sys.stdin)
915 print(f\" name: {d.get('displayName','?')}\")
916 print(f\" desc: {d.get('description','')[:120]}\")
917 print(f\" followers: {d.get('followersCount',0)}\")
918 print(f\" avatar: {d.get('avatar','none')[:80]}\")
919 " 2>/dev/null
920 done)
921 ⎿ Error: Sibling tool call errored
922
923⏺ Bash(curl -s "https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts?q=atproto&limit=5&sort=top" 2>/dev/null | python3 -m json.tool | head -40)
924 ⎿ Expecting value: line 1 column 1 (char 0)
925
926⏺ Bash(curl -sv "https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts?q=atproto&limit=3&sort=top" 2>&1 | head -30)
927 ⎿ * Host public.api.bsky.app:443 was resolved.
928 * IPv6: (none)
929 * IPv4: 169.150.236.100
930 * Trying 169.150.236.100:443...
931 * Connected to public.api.bsky.app (169.150.236.100) port 443
932 * ALPN: curl offers h2,http/1.1
933 * (304) (OUT), TLS handshake, Client hello (1):
934 } [324 bytes data]
935 * CAfile: /etc/ssl/cert.pem
936 * CApath: none
937 * (304) (IN), TLS handshake, Server hello (2):
938 { [122 bytes data]
939 * (304) (IN), TLS handshake, Unknown (8):
940 { [19 bytes data]
941 * (304) (IN), TLS handshake, Certificate (11):
942 { [3979 bytes data]
943 * (304) (IN), TLS handshake, CERT verify (15):
944 { [264 bytes data]
945 * (304) (IN), TLS handshake, Finished (20):
946 { [52 bytes data]
947 * (304) (OUT), TLS handshake, Finished (20):
948 } [52 bytes data]
949 * SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF
950 * ALPN: server accepted h2
951 * Server certificate:
952 * subject: CN=public.api.bsky.app
953 * start date: Feb 24 17:06:21 2026 GMT
954 * expire date: May 25 17:06:20 2026 GMT
955 * subjectAltName: host "public.api.bsky.app" matched cert's "public.api.bsky.app"
956 * issuer: C=US; O=Let's Encrypt; CN=R12
957
958⏺ Bash(curl -s "https://public.api.bsky.app/xrpc/app.bsky.feed.searchPosts?q=atproto&limit=3&sort=top" 2>/dev/null | head -200)
959 ⎿ <html><head><title>403 Forbidden</title><link href='//fonts.bunny.net/css?family=Rubik:300,400,500' rel='stylesheet' type='text/css'><style>html, body { width: 100%; margin: 0;
960 padding: 0; text-align: center; font-family: 'Rubik'; background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iMjg4MHB4
961 IiBoZWlnaHQ9IjE0MjRweCIgdmlld0JveD0iMCAwIDI4ODAgMTQyNCIgdmVyc2lvbj0iMS4xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIj4
962 KICAgIDxkZWZzPgogICAgICAgIDxyYWRpYWxHcmFkaWVudCBjeD0iNDguNDU0MDQyMiUiIGN5PSIyNy4wMTE5NjQ1JSIgZng9IjQ4LjQ1NDA0MjIlIiBmeT0iMjcuMDExOTY0NSUiIHI9IjcwLjg3MDg1MTQlIiBncmFkaWVudFRyYW5zZm
963 9ybT0idHJhbnNsYXRlKDAuNDg0NTQwLDAuMjcwMTIwKSxzY2FsZSgwLjQ5NDQ0NCwxKSxyb3RhdGUoOTApLHRyYW5zbGF0ZSgtMC40ODQ1NDAsLTAuMjcwMTIwKSIgaWQ9InJhZGlhbEdyYWRpZW50LTEiPgogICAgICAgICAgICA8c3Rvc
964 CBzdG9wLWNvbG9yPSIjMDAyNjQ5IiBvZmZzZXQ9IjAlIj48L3N0b3A+CiAgICAgICAgICAgIDxzdG9wIHN0b3AtY29sb3I9IiMwNTFGMzciIG9mZnNldD0iMTAwJSI+PC9zdG9wPgogICAgICAgIDwvcmFkaWFsR3JhZGllbnQ+CiAgICAg
965 ICAgPHJlY3QgaWQ9InBhdGgtMiIgeD0iMCIgeT0iMCIgd2lkdGg9IjI4ODAiIGhlaWdodD0iMTQyNCI+PC9yZWN0PgogICAgPC9kZWZzPgogICAgPGcgaWQ9IlBhZ2UtMSIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIGZpbGw
966 9Im5vbmUiIGZpbGwtcnVsZT0iZXZlbm9kZCI+CiAgICAgICAgPGcgaWQ9IkhvbWVwYWdlLUNvcHkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAsIC01Mjk1KSI+CiAgICAgICAgICAgIDxnIGlkPSJHcm91cC0xNyIgdHJhbnNmb3JtPSJ0cm
967 Fuc2xhdGUoMCwgNTI5NSkiPgogICAgICAgICAgICAgICAgPGcgaWQ9Ikdyb3VwLTM0Ij4KICAgICAgICAgICAgICAgICAgICA8bWFzayBpZD0ibWFzay0zIiBmaWxsPSJ3aGl0ZSI+CiAgICAgICAgICAgICAgICAgICAgICAgIDx1c2Uge
968 Gxpbms6aHJlZj0iI3BhdGgtMiI+PC91c2U+CiAgICAgICAgICAgICAgICAgICAgPC9tYXNrPgogICAgICAgICAgICAgICAgICAgIDx1c2UgaWQ9Ik1hc2siIGZpbGw9InVybCgjcmFkaWFsR3JhZGllbnQtMSkiIHhsaW5rOmhyZWY9IiNw
969 YXRoLTIiPjwvdXNlPgogICAgICAgICAgICAgICAgPC9nPgogICAgICAgICAgICA8L2c+CiAgICAgICAgPC9nPgogICAgPC9nPgo8L3N2Zz4='); background-repeat: no-repeat; background-position: bottom center;
970 background-size: cover; color: white; height: 100%; background-color: #051f37; } h1 {margin-bottom: 0px;font-weight: bold;font-size: 140px;font-weight: 500;padding-top:
971 130px;margin-bottom: -35px;}h2 {font-size: 45px;color: white; font-weight: 200;}</style></head><body><div id='content'><h1 style='margin-bottom:
972 -35px;'>403</h1><h2>Forbidden</h2></div></body></html>
973
974
975⏺ Search is rate limited. That's fine — I'll use the Bluesky feeds API and direct record fetching instead.
976
977⏺ Bash(# Get some profiles
978 for handle in "jay.bsky.team" "pfrazee.com" "why.bsky.world"; do
979 echo "=== $handle ==="
980 curl -s "https://public.api.bsky.app/xrpc/app.bsky.actor.getProfile?actor=$handle" | python3 -c "
981 import json,sys
982 d=json.load(sys.stdin)
983 print(f\" name: {d.get('displayName','?')}\")
984 print(f\" desc: {d.get('description','')[:120]}\")
985 print(f\" followers: {d.get('followersCount',0)}\")
986 print(f\" did: {d.get('did','?')}\")
987 " 2>/dev/null
988 done)
989 ⎿ === jay.bsky.team ===
990 name: Jay 🦋
991 desc: CEO of Bluesky, steward of AT Protocol.
992
993 dec/acc 🌱 🪴 🌳
994 followers: 595687
995 did: did:plc:oky5czdrnfjpqslsw2a5iclo
996 === pfrazee.com ===
997 name: Paul Frazee
998 desc: CTO at Bluesky. Not a nerd.
999 followers: 308141
1000 did: did:plc:ragtjsm2j2vknwkz3zp4oxrd
1001 === why.bsky.world ===
1002 name: ?
1003 desc:
1004 followers: 0
1005 did: ?
1006
1007⏺ Bash(# Get some popular feeds and discover the atmosphere
1008 curl -s "https://public.api.bsky.app/xrpc/app.bsky.feed.getTimeline?limit=1" 2>/dev/null | head -5
1009 echo "---"
1010 # Try discover feed
1011 curl -s "https://public.api.bsky.app/xrpc/app.bsky.unspecced.getPopularFeedGenerators?limit=10" 2>/dev/null | python3 -c "
1012 import json,sys
1013 d=json.load(sys.stdin)
1014 for f in d.get('feeds',[]):
1015 print(f\"{f.get('displayName','?'):30} likes={f.get('likeCount',0):>6} by {f['creator']['handle']}\")
1016 " 2>/dev/null)
1017 ⎿ {"error":"AuthMissing","message":"Authentication Required"}---
1018 Discover likes= 38745 by bsky.app
1019 Popular With Friends likes= 40894 by bsky.app
1020 Mutuals likes= 28510 by skyfeed.xyz
1021 Science likes= 29186 by bossett.social
1022 Artists: Trending likes= 32632 by bsky.art
1023 📰 News likes= 23893 by aendra.com
1024 Blacksky likes= 28045 by rude1.blacksky.team
1025 For You likes= 44903 by spacecowboy17.bsky.social
1026 What's Hot Classic likes= 10380 by bsky.app
1027
1028⏺ Bash(# Get recent posts from the discover feed (public)
1029 curl -s "https://public.api.bsky.app/xrpc/app.bsky.feed.getFeed?feed=at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot&limit=10" 2>/dev/null | python3 -c "
1030 import json,sys
1031 d=json.load(sys.stdin)
1032 for item in d.get('feed',[]):
1033 p = item['post']
1034 print(f\"@{p['author']['handle']} ({p['author'].get('displayName','?')})\")
1035 print(f\" {p['record']['text'][:150]}\")
1036 print(f\" likes={p.get('likeCount',0)} reposts={p.get('repostCount',0)} replies={p.get('replyCount',0)}\")
1037 print(f\" uri={p['uri']}\")
1038 print()
1039 " 2>/dev/null | head -60)
1040 ⎿ @bgormley.bsky.social (Brian Gormley D7)
1041 Beautiful, crisp cold morning in Tolka Valley Park. Dipper, Chiffchaff, Redpoll and Reed Bunting so far.
1042 likes=2166 reposts=143 replies=32
1043 uri=at://did:plc:223cpcieytdlbmzovhs2ypg2/app.bsky.feed.post/3mfvvs5dyxs2o
1044
1045 @setouchiandore.bsky.social (瀬戸内アンドレ)
1046 おはようございます
1047 仮眠からの目覚め
1048 今日から3月
1049 年度末の戦い(笑)
1050 5連勤の1回戦目
1051 それでは参ります
1052
1053 #AIart
1054 #瀬戸内きらり
1055 #花とともに
1056 likes=10 reposts=2 replies=0
1057 uri=at://did:plc:227elnhync3rwi66l2fgw3n2/app.bsky.feed.post/3mfygdm52fs24
1058
1059 @justinanumudu.bsky.social (Justin Anumudu)
1060 Congratulations to #LeBronJames on joining #KobeBryant as the only players in #NBAHistory to make 1,000 career 3-pointers with the #Lakers. 🙂💛💜💛💜💛💜💛💜💛
1061 likes=611 reposts=52 replies=22
1062 uri=at://did:plc:2fil3vrrymlpa5uspsfgo37i/app.bsky.feed.post/3mfxrhmn4nc2d
1063
1064 @lauren-oujiri.bsky.social (lauren oujiri)
1065 for you
1066 🤍🕊️
1067
1068 #ArtistsOnBlueSky #BlueSkyArtShow #Photography #WomensArtBlueSky #PhotographersOnBlueSky #Flowers #Rose #OregonPhotographer #Oregon
1069 likes=229 reposts=39 replies=17
1070 uri=at://did:plc:23j7hkwlvwosxuu32azggqih/app.bsky.feed.post/3mfwge4r7h22n
1071
1072 @forbes.com (Forbes)
1073 Gas could reach a national average above $3 per gallon for the first time this year, as analysts anticipate crude oil prices will jump next week.
1074 likes=1487 reposts=640 replies=310
1075 uri=at://did:plc:2w45zyhuklwihpdc7oj3mi63/app.bsky.feed.post/3mfx4csfsby2i
1076
1077 @simonewunderlich.bsky.social (Simone W.)
1078 Guten Morgen!
1079 (Kilefjorden, Norwegen, Sommer 2025)
1080 likes=305 reposts=22 replies=21
1081 uri=at://did:plc:22gc24n3t57an6etset6jp5b/app.bsky.feed.post/3mfvmwuoyxk2e
1082
1083 @gingercatdusty.bsky.social (Ginger Cat Dusty)
1084 Cookie update. He is such a sweetheart. Very accepting of his indoor life. Seems ready to become a mushy household love bug. Not quite there yet. Stil
1085 likes=7377 reposts=352 replies=293
1086 uri=at://did:plc:223xmbonln2z6txstz6xfexd/app.bsky.feed.post/3mfwlu5lq2c2k
1087
1088 @hollyolsonbcpa.bsky.social (Holly Olson, BCPA)
1089 Peaceful coexistence 🕊️ #caturday
1090 likes=4134 reposts=203 replies=98
1091 uri=at://did:plc:226emmaran4fqn5jvhfkp2zu/app.bsky.feed.post/3mfwyiddkds2q
1092
1093 @theverge.com (The Verge)
1094 Tenways nearly perfects the shareable city e-bike
1095 likes=29 reposts=5 replies=4
1096 uri=at://did:plc:7exlcsle4mjfhu3wnhcgizz6/app.bsky.feed.post/3mfvrkqp4q32q
1097
1098 @kriswernowsky.com (Kris Wernowsky)
1099 Dogs are tired of the news. They want to see more great dogs. The more tired, the better.
1100
1101⏺ Good — I have a rich picture of what's happening. Now let me build the app. I'll create a compelling, design-forward page that pulls live data.
1102
1103⏺ Read(src/app.html)
1104 ⎿ Read 12 lines
1105
1106⏺ Update(/Users/nate/tangled.sh/@zzstoatzz.io/demo/src/app.html)
1107 ⎿ Added 2 lines
1108 3 <head>
1109 4 <meta charset="utf-8" />
1110 5 <meta name="viewport" content="width=device-width, initial-scale=1" />
1111 6 + <link rel="preconnect" href="https://fonts.bunny.net" />
1112 7 + <link href="https://fonts.bunny.net/css?family=inter:300,400,500,600,700|space-grotesk:400,500,600,700" rel="stylesheet" />
1113 8 %sveltekit.head%
1114 9 </head>
1115 10 <body data-sveltekit-preload-data="hover">
1116
1117⏺ Now let me create the layout file and the main page. I'll write the API helper first, then the Svelte components.
1118
1119⏺ Bash(mkdir -p /Users/nate/tangled.sh/@zzstoatzz.io/demo/src/lib)
1120 ⎿ Done
1121
1122⏺ Write(/Users/nate/tangled.sh/@zzstoatzz.io/demo/src/lib/atmosphere.ts)
1123 ⎿ Wrote 141 lines to /Users/nate/tangled.sh/@zzstoatzz.io/demo/src/lib/atmosphere.ts
1124 const SLINGSHOT = 'https://slingshot.microcosm.blue/xrpc';
1125 const CONSTELLATION = 'https://constellation.microcosm.blue/xrpc';
1126 const BSKY_PUBLIC = 'https://public.api.bsky.app/xrpc';
1127
1128 export interface Identity {
1129 did: string;
1130 handle: string;
1131 pds: string;
1132 }
1133
1134 export interface Profile {
1135 did: string;
1136 handle: string;
1137 displayName: string;
1138 description: string;
1139 avatar: string;
1140 followersCount: number;
1141 followsCount: number;
1142 postsCount: number;
1143 }
1144
1145 export interface FeedPost {
1146 uri: string;
1147 author: Profile;
1148 text: string;
1149 likeCount: number;
1150 repostCount: number;
1151 replyCount: number;
1152 createdAt: string;
1153 embed?: {
1154 images?: { thumb: string; alt: string }[];
1155 };
1156 }
1157
1158 export interface FeedGenerator {
1159 uri: string;
1160 displayName: string;
1161 description: string;
1162 likeCount: number;
1163 creator: { handle: string; displayName: string };
1164 }
1165
1166 export async function resolveIdentity(identifier: string): Promise<Identity> {
1167 const res = await fetch(
1168 `${SLINGSHOT}/blue.microcosm.identity.resolveMiniDoc?identifier=${encodeURIComponent(identifier)}`
1169 );
1170 return res.json();
1171 }
1172
1173 export async function getProfile(actor: string): Promise<Profile> {
1174 const res = await fetch(
1175 `${BSKY_PUBLIC}/app.bsky.actor.getProfile?actor=${encodeURIComponent(actor)}`
1176 );
1177 return res.json();
1178 }
1179
1180 export async function getLikeCount(atUri: string): Promise<number> {
1181 const res = await fetch(
1182 `${CONSTELLATION}/blue.microcosm.links.getBacklinksCount?subject=${encodeURIComponent(atUri)}&source=${encodeURIComponent('app.bsky.feed.like:subject.uri')}`
1183 );
1184 const data = await res.json();
1185 return data.total;
1186 }
1187
1188 export async function getReplyCount(atUri: string): Promise<number> {
1189 const res = await fetch(
1190 `${CONSTELLATION}/blue.microcosm.links.getBacklinksCount?subject=${encodeURIComponent(atUri)}&source=${encodeURIComponent('app.bsky.feed.post:reply.parent.uri')}`
1191 );
1192 const data = await res.json();
1193 return data.total;
1194 }
1195
1196 export async function getFollowerCount(did: string): Promise<number> {
1197 const res = await fetch(
1198 `${CONSTELLATION}/blue.microcosm.links.getBacklinksCount?subject=${encodeURIComponent(did)}&source=${encodeURIComponent('app.bsky.graph.follow:subject')}`
1199 );
1200 const data = await res.json();
1201 return data.total;
1202 }
1203
1204 export async function getWhatsHot(limit = 10): Promise<FeedPost[]> {
1205 const feedUri = 'at://did:plc:z72i7hdynmk6r22z27h6tvur/app.bsky.feed.generator/whats-hot';
1206 const res = await fetch(
1207 `${BSKY_PUBLIC}/app.bsky.feed.getFeed?feed=${encodeURIComponent(feedUri)}&limit=${limit}`
1208 );
1209 const data = await res.json();
1210 return (data.feed || []).map((item: any) => {
1211 const p = item.post;
1212 return {
1213 uri: p.uri,
1214 author: {
1215 did: p.author.did,
1216 handle: p.author.handle,
1217 displayName: p.author.displayName || p.author.handle,
1218 description: p.author.description || '',
1219 avatar: p.author.avatar || '',
1220 followersCount: p.author.followersCount || 0,
1221 followsCount: p.author.followsCount || 0,
1222 postsCount: p.author.postsCount || 0
1223 },
1224 text: p.record?.text || '',
1225 likeCount: p.likeCount || 0,
1226 repostCount: p.repostCount || 0,
1227 replyCount: p.replyCount || 0,
1228 createdAt: p.record?.createdAt || '',
1229 embed: p.embed?.images
1230 ? {
1231 images: p.embed.images.map((img: any) => ({
1232 thumb: img.thumb,
1233 alt: img.alt || ''
1234 }))
1235 }
1236 : undefined
1237 };
1238 });
1239 }
1240
1241 export async function getPopularFeeds(limit = 10): Promise<FeedGenerator[]> {
1242 const res = await fetch(
1243 `${BSKY_PUBLIC}/app.bsky.unspecced.getPopularFeedGenerators?limit=${limit}`
1244 );
1245 const data = await res.json();
1246 return (data.feeds || []).map((f: any) => ({
1247 uri: f.uri,
1248 displayName: f.displayName,
1249 description: f.description || '',
1250 likeCount: f.likeCount || 0,
1251 creator: {
1252 handle: f.creator.handle,
1253 displayName: f.creator.displayName || f.creator.handle
1254 }
1255 }));
1256 }
1257
1258 export function timeAgo(dateStr: string): string {
1259 const seconds = Math.floor((Date.now() - new Date(dateStr).getTime()) / 1000);
1260 if (seconds < 60) return 'just now';
1261 if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
1262 if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
1263 return `${Math.floor(seconds / 86400)}d ago`;
1264 }
1265
1266
1267⏺ Now the main page. I'll build it with the svelte file editor agent.
1268
1269⏺ Write(/Users/nate/tangled.sh/@zzstoatzz.io/demo/src/routes/+layout.svelte)
1270 ⎿ Error: File has not been read yet. Read it first before writing to it.
1271
1272⏺ Write(/Users/nate/tangled.sh/@zzstoatzz.io/demo/src/routes/+layout.svelte)
1273 ⎿ Error: File has not been read yet. Read it first before writing to it.
1274
1275⏺ Bash(ls /Users/nate/tangled.sh/@zzstoatzz.io/demo/src/routes/)
1276 ⎿ +layout.svelte
1277 +page.svelte
1278
1279⏺ Read(src/routes/+layout.svelte)
1280 ⎿ Read 12 lines
1281
1282⏺ svelte:svelte-file-editor(Write layout.svelte file)
1283 ⎿ Done (4 tool uses · 16.6k tokens · 16s)
1284 (ctrl+o to expand)
1285
1286⏺ Good. Now the main page — this is the big one. Let me write it in a single file with all sections.
1287
1288⏺ svelte:svelte-file-editor(Write main page component)
1289 ⎿ Done (12 tool uses · 64.8k tokens · 6m 14s)
1290 (ctrl+o to expand)
1291
1292⏺ Now let me also add the prerender config for static site generation and test the build.
1293
1294⏺ Write(/Users/nate/tangled.sh/@zzstoatzz.io/demo/src/routes/+layout.ts)
1295 ⎿ Wrote 2 lines to /Users/nate/tangled.sh/@zzstoatzz.io/demo/src/routes/+layout.ts
1296 export const prerender = true;
1297 export const ssr = false;
1298
1299
1300⏺ Bash(npm run build 2>&1)
1301 ⎿ > @zzstoatzz.io/demo@0.0.1 build
1302 > vite build
1303
1304 vite v7.3.1 building ssr environment for production...
1305 transforming...
1306 ✓ 192 modules transformed.
1307 rendering chunks...
1308 vite v7.3.1 building client environment for production...
1309 transforming...
1310 ✓ 153 modules transformed.
1311 rendering chunks...
1312 computing gzip size...
1313 .svelte-kit/output/client/_app/version.json 0.03 kB │ gzip: 0.05 kB
1314 .svelte-kit/output/client/.vite/manifest.json 2.99 kB │ gzip: 0.59 kB
1315 .svelte-kit/output/client/_app/immutable/assets/2.B1ZWvA-n.css 8.60 kB │ gzip: 2.18 kB
1316 .svelte-kit/output/client/_app/immutable/entry/start.DbqxCpZo.js 0.08 kB │ gzip: 0.09 kB
1317 .svelte-kit/output/client/_app/immutable/chunks/btG01S4X.js 0.32 kB │ gzip: 0.25 kB
1318 .svelte-kit/output/client/_app/immutable/chunks/DNRrmDCL.js 0.44 kB │ gzip: 0.32 kB
1319 .svelte-kit/output/client/_app/immutable/nodes/1.grphhOL2.js 1.00 kB │ gzip: 0.59 kB
1320 .svelte-kit/output/client/_app/immutable/nodes/0.Cfk9YyAO.js 1.08 kB │ gzip: 0.70 kB
1321 .svelte-kit/output/client/_app/immutable/chunks/CTLCUQ6f.js 1.23 kB │ gzip: 0.70 kB
1322 .svelte-kit/output/client/_app/immutable/chunks/BmjRXfy5.js 1.49 kB │ gzip: 0.66 kB
1323 .svelte-kit/output/client/_app/immutable/chunks/ZWi2eq9H.js 6.38 kB │ gzip: 2.91 kB
1324 .svelte-kit/output/client/_app/immutable/entry/app.Bf8EDJfB.js 6.82 kB │ gzip: 3.19 kB
1325 .svelte-kit/output/client/_app/immutable/nodes/2.B7w7-4b-.js 18.44 kB │ gzip: 7.33 kB
1326 .svelte-kit/output/client/_app/immutable/chunks/DArY1D7I.js 22.49 kB │ gzip: 8.90 kB
1327 .svelte-kit/output/client/_app/immutable/chunks/BnZGWh95.js 26.10 kB │ gzip: 10.27 kB
1328 ✓ built in 487ms
1329 .svelte-kit/output/server/.vite/manifest.json 2.87 kB
1330 .svelte-kit/output/server/_app/immutable/assets/_page.B1ZWvA-n.css 8.60 kB
1331 .svelte-kit/output/server/chunks/false.js 0.05 kB
1332 .svelte-kit/output/server/entries/pages/_layout.ts.js 0.07 kB
1333 .svelte-kit/output/server/internal.js 0.35 kB
1334 .svelte-kit/output/server/entries/pages/_layout.svelte.js 0.58 kB
1335 .svelte-kit/output/server/chunks/environment.js 0.62 kB
1336 .svelte-kit/output/server/chunks/utils.js 1.15 kB
1337 .svelte-kit/output/server/entries/fallbacks/error.svelte.js 1.35 kB
1338 .svelte-kit/output/server/chunks/internal.js 3.35 kB
1339 .svelte-kit/output/server/chunks/exports.js 7.03 kB
1340 .svelte-kit/output/server/entries/pages/_page.svelte.js 9.26 kB
1341 .svelte-kit/output/server/remote-entry.js 19.03 kB
1342 .svelte-kit/output/server/chunks/shared.js 27.07 kB
1343 .svelte-kit/output/server/chunks/index.js 37.74 kB
1344 .svelte-kit/output/server/chunks/root.js 78.14 kB
1345 .svelte-kit/output/server/index.js 123.26 kB
1346 ✓ built in 1.74s
1347
1348 Run npm run preview to preview your production build locally.
1349
1350 > Using @sveltejs/adapter-static
1351 Wrote site to "build"
1352 ✔ done
1353 ⎿ (timeout 30s)
1354
1355⏺ Build succeeds. Let me verify the output.
1356
1357⏺ Bash(ls /Users/nate/tangled.sh/@zzstoatzz.io/demo/build/ && echo "---" && head -30 /Users/nate/tangled.sh/@zzstoatzz.io/demo/build/index.html)
1358 ⎿ _app
1359 index.html
1360 robots.txt
1361 … +31 lines (ctrl+o to expand)
1362
1363⏺ All relative paths — good for wisp. Let me verify the paths are truly relative (no leading /).
1364
1365⏺ Bash(grep -c 'href="/' /Users/nate/tangled.sh/@zzstoatzz.io/demo/build/index.html; grep -c 'src="/' /Users/nate/tangled.sh/@zzstoatzz.io/demo/build/index.html; grep…)
1366 ⎿ 0
1367 0
1368 0
1369
1370⏺ Zero absolute paths. Now let me deploy to wisp.
1371
1372⏺ Bash(ls /tmp/wisp-cli 2>/dev/null && echo "exists" || echo "need to download")
1373 ⎿ /tmp/wisp-cli
1374 exists
1375
1376⏺ Bash(ls /tmp/wisp-oauth-session.json 2>/dev/null && echo "session exists" || echo "need auth")
1377 ⎿ need auth
1378
1379⏺ Bash(/tmp/wisp-cli login zzstoatzz.io 2>&1)
1380 ⎿ Error: Exit code 1
1381 error: too many arguments for 'deploy'. Expected 1 argument but got 2.
1382
1383 error: too many arguments for 'deploy'. Expected 1 argument but got 2.
1384
1385⏺ Bash(/tmp/wisp-cli --help 2>&1)
1386 ⎿ Usage: wisp-cli [options] [command]
1387
1388 CLI for wisp.place - deploy static sites to the AT Protocol
1389
1390 Options:
1391 -V, --version output the version number
1392 -h, --help display help for command
1393
1394 Commands:
1395 deploy [options] [handle] Deploy a static site to wisp.place
1396 pull [options] <handle> Download a site from wisp.place to a local
1397 directory
1398 serve [options] <handle> Serve a site locally with live updates from
1399 firehose
1400 list [options] List sites and domains from wisp XRPC routes
1401 domain|domains [options] Manage domains with wisp XRPC
1402 site [options] Manage sites with wisp XRPC
1403 logout [options] Clear stored OAuth sessions
1404 help [command] display help for command
1405
1406⏺ Bash(/tmp/wisp-cli deploy --help 2>&1)
1407 ⎿ Usage: wisp-cli deploy [options] [handle]
1408
1409 Deploy a static site to wisp.place
1410
1411 Options:
1412 -p, --path <path> Directory to deploy
1413 -s, --site <name> Site name (defaults to directory name)
1414 --directory Enable directory listing
1415 --spa Enable SPA mode (serve index.html for all routes)
1416 -c, --concurrency <n> Number of concurrent uploads (backs off to 2 on rate
1417 limit) (default: "3")
1418 --force-gzip Force gzip compression for all files regardless of type
1419 --password <password> App password for headless authentication
1420 --store <path> OAuth session store path
1421 -y, --yes Skip confirmation prompts
1422 -h, --help display help for command
1423
1424⏺ Bash(/tmp/wisp-cli deploy zzstoatzz.io --path ./build --site atmosphere --spa -y 2>&1)
1425 ⎿ │
1426 ◒ Authenticating◐ Authenticating◓ Starting OAuth flow for zzstoatzz.io◑ Starting OAuth flow for zzstoatzz.io◒ Starting OAuth flow for zzstoatzz.io
1427 If browser does not open, visit: https://pds.zzstoatzz.io/oauth/authorize?client_id=http%3A%2F%2Flocalhost%3Fredirect_uri%3Dhttp%253A%252F%252F127.0.0.1%253A4000%252Foauth%252Fcal
1428 lback%26scope%3Datproto%2Brepo%253Aplace.wisp.fs%2Brepo%253Aplace.wisp.subfs%2Brepo%253Aplace.wisp.settings%2Bblob%253A*%252F*&request_uri=urn%3Aietf%3Aparams%3Aoauth%3Arequest_ur
1429 i%3Areq-961b16db7934dbe206f65be082b19f56
1430
1431 ◐ Waiting for OAuth callback◓ Waiting for OAuth callback◑ Waiting for OAuth callback◒ Waiting for OAuth callback.◐ Waiting for OAuth callback.◓ Waiting for OAuth callback.◑
1432 Waiting for OAuth callback.◒ Waiting for OAuth callback.◐ Waiting for OAuth callback.◓ Waiting for OAuth callback.◑ Waiting for OAuth callback.◒ Waiting for OAuth
1433 callback..◐ Waiting for OAuth callback..◓ Waiting for OAuth callback..◑ Waiting for OAuth callback..◒ Waiting for OAuth callback..◐ Waiting for OAuth callback..◓ Waiting for
1434 OAuth callback..◑ Waiting for OAuth callback..◒ Waiting for OAuth callback...◐ Waiting for OAuth callback...◓ Waiting for OAuth callback...◇ ✓ Authenticated as zzstoatzz.io
1435 (did:plc:xbtmt2zjwlrfegqvch7fboei)
1436
1437 Deploying atmosphere from ./build
1438
1439 │
1440 ◇ ✓ Found 17 files (93.81 KB)
1441 │
1442 ◇ ✓ Found existing site, will reuse unchanged files
1443 │
1444 ◒ Processing files: 3/17 (2 uploaded, 1 reused)◐ Processing files: 6/17 (5 uploaded, 1 reused)◓ Processing files: 9/17 (8 uploaded, 1 reused)◑ Processing files: 10/17 (9
1445 uploaded, 1 reused)◒ Processing files: 12/17 (11 uploaded, 1 reused)◇ ✓ Processed 17 files (15 uploaded, 2 reused)
1446 │
1447 ◇ ✓ Created manifest record
1448 │
1449 ◇ ✓ Created settings record
1450
1451 URI: at://did:plc:xbtmt2zjwlrfegqvch7fboei/place.wisp.fs/atmosphere
1452 URL: https://sites.wisp.place/zzstoatzz.io/atmosphere
1453 URL: https://sites.wisp.place/did:plc:xbtmt2zjwlrfegqvch7fboei/atmosphere
1454
1455 ✓ Deployed successfully!
1456
1457 ⎿ (timeout 2m)
1458
1459⏺ Deployed. Live at https://sites.wisp.place/zzstoatzz.io/atmosphere
1460
1461 Here's what it does:
1462
1463 - Hero — animated count of likes on trending posts, pulled live from the Bluesky public API
1464 - The pitch — four cards explaining the atproto value prop in plain language: you own your identity, your data travels with you, one protocol / many apps, the exit door is always open
1465 - Identity resolver — type any handle (e.g. jay.bsky.team) and watch it resolve via Slingshot to a cryptographic DID, showing their PDS host, avatar, bio, and follower count
1466 - Live feed — trending posts from the atmosphere, fetched in real time from the What's Hot feed generator
1467 - Algorithms you choose — shows popular community-built feed generators with subscriber counts
1468 - Philosophy section — the Martin quote that Daniel Holmgren cited as "atproto ethos" at ATmosphereConf, followed by a plain-english explanation of credible exit and lazy trust
1469 - CTA — links to Bluesky, the protocol docs, and the ethos talk transcript on Leaflet
1470
1471 All data is fetched client-side from three open, unauthenticated APIs (Slingshot, Constellation, Bluesky public), and the page itself is hosted on the atmosphere via wisp.place.