One Post, Two Blogs

· PABLO'S DEVLOG


I keep two blogs. One lives on prose.sh — terminal-native, plain text, no dashboard. The other runs on Ghost (this one here), which is prettier and friendlier for longer pieces. I like both for different reasons, and for a while I told myself I'd just keep them in sync by hand.

You already know how that ended.

Maintaining the same post in two places, by hand, is exactly the kind of chore I quietly abandon around week two. So instead of pretending I'm more disciplined than I am, I automated it. This is the story of how that automation grew — from a single scp to a little bridge that keeps both blogs in sync on its own, and even lets me publish by sending an email.

This is the part I love, so let me start here. prose.sh has no admin panel. You write a markdown file and copy it to the server over SSH:

1scp my-first-post.md me@prose.sh:/

The file is just text with a tiny bit of frontmatter on top:

1---
2title: My First Post
3date: 2026-06-27
4---
5
6Hello from a plain text file.

That's the whole thing. Your editor, your files, zero lock-in.

Doing this once is delightful. Doing it for every post — remembering the filename, the frontmatter, the exact scp line — while also keeping a Ghost blog alive, on a week where work ate every evening... the friction adds up. And friction is where my good intentions go to die.

I wanted to write once and have it show up in both places. That's it.

First I did the obvious thing and wrapped the boring parts in a small CLI. List my posts, create a new one, publish — all over the same SSH the manual way used, just without me typing the incantation every time.

1blog publish my-first-post.md

There was one genuinely annoying detail worth mentioning: my SSH key lived on a network share, and OpenSSH flat-out refuses keys with "loose" permissions. So the script learned to copy the key to a safe local spot and lock it down before connecting. Small thing, but it's the difference between "works on my machine" and "works on every machine."

This already made my life better. But it still meant I had to run something, and it still treated the two blogs as two separate chores.

The real shift was a decision, not code: Ghost is canonical, prose is a mirror. I write in Ghost, and prose should just... follow.

This is an old idea with an ugly acronym — POSSE: Publish on your Own Site, Syndicate Elsewhere. You keep one home for your writing and let copies flow outward automatically.

The two halves were already there, waiting to be introduced:

All that was missing was a small translator in the middle.

So I wrote one. A tiny service in a container. When I publish in Ghost, it:

  1. catches the webhook,
  2. converts the post's HTML into clean Markdown,
  3. writes the frontmatter for me (title and date — I don't even bother with excerpts),
  4. and scps the file to prose.
1   Write in Ghost
2        │  webhook: "post published"  (HTML)
34 ┌───────────────────┐
5 │   bridge service  │   HTML ──► Markdown ──► add frontmatter
6 └───────────────────┘
7        │  scp
89    prose.sh  ──►  notes.example.com

I publish in one place. A few seconds later the same post is sitting on prose, formatted correctly, no copy-paste, no second chore.

Plot twist: posting by email #

Then I got greedy. Some of my best ideas show up when I'm nowhere near a laptop — in line somewhere, on the couch, on my phone. I wanted to fire off a quick post the laziest way imaginable: send an email.

So I added one more door. I email a plain message — the subject becomes the title, the body becomes the post — and it lands on both blogs.

The trick that made it clean: the email doesn't talk to prose at all. It just creates a Ghost post through Ghost's API. Then Ghost's normal webhook does the rest, exactly like a post I'd written by hand. One road to prose, not two.

 1   Write in Ghost ───────────────┐
 2                                 │ webhook
 3   Send an email                 │
 4        │                        ▼
 5        ▼                ┌────────────────┐
 6  inbound email  ──POST─►│ bridge service │──API──► create post in Ghost
 7   (mail provider)       │                │◄─webhook─┘
 8                         └────────────────┘
 9                                  │ scp
1011                              prose.sh

One more wrinkle: my little box sits at home behind a connection with no public IP, so a stranger on the internet (Ghost's webhook, the mail provider) can't reach it directly. I tunnel those requests in through a cheap VPS, which terminates HTTPS and forwards them down to the container. The box never has to be "on the internet" — it just keeps a quiet outbound tunnel open.

1  internet ──► tiny VPS (HTTPS) ──tunnel──► home box ──► bridge service

Few things that only became obvious after building it:

It's the kind of boring magic I like: invisible when it works, and it mostly just works.

And yes. It's working.

See you around.

last updated: