it flounders a bit bc i accidentally rmd some important microcosm detail that i've now restored
wisp deploy demo
1471 lines 98 kB view raw
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![Sunset over the desert in New 388 Mexico.](https://pfmksuq9g71x0dz6.public.blob.vercel-storage.com/regrets-of-a-technical-communicator-1735785667142-MAWByS6PjoygEcjT5o987qUMvWcZ0l.jpeg)\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 14391440 ◇ ✓ Found 17 files (93.81 KB) 14411442 ◇ ✓ Found existing site, will reuse unchanged files 14431444 ◒ 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) 14461447 ◇ ✓ Created manifest record 14481449 ◇ ✓ 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.