I made a Ghost blog export to offprint script that I call "GhostOff" and it's available on Tangled:
This also happens to be one of my first experiments with agentic coding1.
I spent an hour or so in plan mode2, explaining how it should work, detailing the .env file and credentials that would be needed, requiring the use of the atcute library by , attaching the Lexicon Garden MCP server3, and preparing a test account and test publication. The Ghost Content API is pretty standardized. I walked through some of the things in Ghost Cards that might not have offprint block equivalents4.
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.
npm run dev to start the flow, you're going to need a couple of things filled into .env.GhostOff OAuth for CLI Flow
Just a quick couple of screenshots with URLs. npm run dev to start the flow, you're going to need a couple of things filled into .env.
Depending on your settings, it will pretty much just pop open a browser to the login page of your PDS:
Yes, it is very much using that old classic, transition:generic, because I told it to0. I will tackle OAuth Scopes at some point.
Authentication successful. You may return to the natural environment of your terminal and watch log messages of atproto-ization being visited on your Ghost posts.
I realized in running this again that… the session sticks around. So going to need to work on cleaning that up.
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 .
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 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 .6
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.7
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 --trustI've got caps published to the network. If you want to add OAuth login for your CLI apps, this might work for you.8
Infinite forks. Hug your people. We can get through this together. Let's get busy internecting.