Skip to content

Building GhostOff and our networked, personalized software futures

In which I agentic code Ghost to Offprint script and fall into the future of networked code sharing with Vit

Boris
Jul 4, 20266 min read9 reads
4
3

made a Ghost blog export to offprint script that I call "GhostOff" and it's available on Tangled:

bmann.ca/ghostoff

Migrating content using the Ghost API to Offprint / Standard Site on atproto

This also happens to be one of my first experiments with agentic coding*1.

I spent an hour or so in plan mode*2, explaining how it should work, detailing the .env file and credentials that would be needed, requiring the use of the atcute library by @mary.my.id, attaching the Lexicon Garden MCP server*3, and preparing a test account and test @offprint.app publication. The Ghost Content API is pretty standardized. I walked through some of the things in Ghost Cards that might not have offprint block equivalents*4.

And it worked pretty great first try in build mode. It built everything and then ran the script to test it with the credentials in the env file, downloaded the Ghost posts, and wrote them out as atproto native posts. It verified that the records validated. There are only 26 posts in on the atprotocol.dev Ghost blog, and it ported them all to Offprint! Amazing!*5

There was some confusion about standard.site wrappers then containing app.offprint.content containers and the block structure. At one point it decided to write everything as leaflet lexicon items instead. Yes, the standard site wrapper vs content types is confusing for everyone.

I tweaked a few things and ran the script a couple of more times. I'm...done???

OAuth for the CLI

One of the suggestions on what to do next was moving to OAuth. OAuth??? For a CLI tool???

This is in fact totally possible, and there are lots of very nice auth / CLI / website flows. But maybe tricky? I vaguely remembered that atproto needs 127.0.0.1 rather than just "localhost". Made a plan, told it to go ahead, asked it to write some docs about how to test it manually.

And now we don't need an app password, and we don't need to include a PDS endpoint because it looks it up from the handle.

And that's it. It works and we can start mass exporting Ghost blogs to Offprint: GhostOff indeed!

What's Next

Of course, a CLI script that no one knows about that has to be downloaded from a git forge doesn't solve much of anything in bring people to the Atmosphere. Even with the magic of OAuth, and maybe expanding that localhost trick to gather env variables - enter in your Ghost API key, select or create the publication you want to import to, add support for @pckt.blog @leaflet.pub .

Something that is a desktop app would probably be more accessible. Yeah, it could be a website too.

Substack is a much juicier target, at least for those that aren't actively running monetized newsletters.

I have an Instagram export sitting around somewhere. And a really giant Flickr export…

Networked, Personalized Futures

All of the above sounds like a lot of work. And people might send me PRs, or give me user feedback, or report bugs. Or advocate that I should add supporting their flavour of Markdown to my roadmap which if I build or accept a PR for, I am cursed to maintain forever in "my" codebase.

Nah. I ain't doing all that. Just fork it and tinker with it on your own.

But I actually think the future is way more amazing, and @v-it.org is a glimpse in that direction.

a codebase is not a distribution artifact. a codebase is a living organism that can adapt to each install, and it deserves a living ecosystem. the future is not “one repo, one roadmap.” the future is millions of personalized codebases—each one evolving continuously, shaped by its caretakers’s needs, values, constraints, and taste. vit is the mechanism for that future.

Go read the whole doctrine. And yes, it's built on atproto by @jeremie.com.*7

What do I think this means? Well, I've started using the phrase "infinite forks". And it scares me. I got my start in open source in Drupal. "Don't hack core". Forking core is bad and hard. We go farther when we collect in a repo and work on one codebase together.

So we get our current picture. Not decentralized git, but centralized git forges where we collect issues and discussions and of course the little images of avatars that are people that sometimes come into the real world so you can hug them. And it's been good. It's been great.

But only for coders. For Open Sourcerers. And forks are bad because software is hard.

I've been writing about Networked Orgs for 4 years. Vit feels a lot like this shape of things. Not a git forge single repo hub, with a couple of branches and forks as spokes, downstream/upstream. It is, as described by vit, searching your trusted network for capabilities you can integrate. Evolving constantly.

So, uh, I made a beacon for the ghostoff project and published some caps. I think this is important and we need more people to come try things out.*8

bmann% vit vet ghost-content-export
=== v̇it cap review ===
Review this cap carefully before trusting it.

  Ref:     ghost-content-export
  Title:   Ghost Content API Export
  Author:  did:plc:2cxgdrgtsmrbqnjkwyplmp43

  Fetches public Ghost CMS posts via the Content API 
  and caches them locally for further processing.

--- Text ---
This cap authenticates to a Ghost CMS instance 
using a Content API key, paginates through published posts, 
and writes the raw post data to a local directory. 
It handles pagination, rate limits, and featured image URLs 
so downstream steps can work with clean, cached JSON.
---

To trust this cap, run:

  vit vet ghost-content-export --trust

I've got caps published to the network. If you want to add OAuth login for your CLI apps, this might work for you.*9

Infinite forks. Hug your people. We can get through this together. Let's get busy internecting.


  1. Literally my second thing, and the first one from scratch, from an empty directory. Some more about this and why I made the move now in a recent blog post.
  2. Currently using OpenCode, and as mentioned in the blog post, Umans with open model GLM 5.2. I'll be doing more with our Z-Space Local AI cluster (or cocore!) soon.
  3. Before I connected Lexicon Garden, it was making up app.test.whatever lexicons, so I interrupted it.
  4. There's a table showing the mapping in the GhostOff docs folder.
  5. Screenshot is of posts listed in Skyreader.app - will need to re-run this to make it into the actual Atmosphere Community Blog, which is also waiting on some Offprint atproto sync updates.
  6. And transition:generic is pretty much the same scope as handing a script an app password.
  7. @jeremie.com is the creator of Jabber/XMPP and also happens to be a director of Bluesky PBC. More at sol pbc. And he'll be at Local-First Conf in Berlin with a few new items in his bag of tracks.
  8. I got stuck because I'm using opencode and vit doesn't detect it. Next up contributing to the vit upstream, which is still on boring old github.
  9. Will it? This is, uh, highly alpha software on a distributed network with code hosted on a "knot" that lives on a NixOS minipc at my coworking space. Ping me if this does anything.

Did you enjoy this article?

Recommend it — Standard Reader surfaces well-loved writing to more readers across the network.

Across the AtmosphereDiscussions