---
title: "How to let an AI write your blog without letting it publish"
date: 2026-06-10T13:47:33.654+00:00
url: https://hurricane.works/blog/let-an-ai-write-your-blog-without-publishing
description: "The best material for a developer blog is already in your terminal — but letting an AI write it up means guaranteeing it can't publish on its own. Here's the draft-only pattern we landed on."
author: "Hurricane Works"
categories: ["AI", "Engineering"]
---

# How to let an AI write your blog without letting it publish

If you write a developer blog about AI coding, your best material is already in the terminal: the bug you just chased down, the workaround you found, the trade-off you talked through with the agent. The problem is that writing it up is one more chore at the end of a session, so it never happens and the insight evaporates.

We wanted that write-up to cost a single command. But automating it surfaced a sharper problem — and it's the transferable one: the moment you let an AI write your posts, you need a *guarantee*, not a hope, that it can't publish anything on its own. What follows is the pattern we landed on for letting an AI author content into a live publication safely. It's worth copying even if you never touch Claude Code, because the shape applies to any AI-generated content flowing into something with your name on it.

## What you actually get

At the end of any session, in any project, we type `/blogmaker`. A minute later a fully-drafted post is waiting in our admin, written from what we actually did that session — and there is no path by which the tool could have published it or emailed our list. We review, edit, and ship by hand.

That's the whole pitch: **capture costs one command; releasing always costs a human.** Three stages, a person between each:

1. **`/blogmaker`** — a global command, usable in any project, that reviews the session and writes a post.
2. **A draft-only ingest endpoint** that accepts the post into production — but only ever as a draft.
3. **A dev-only admin** where we read it, edit it, and publish. Publishing is also the only thing that queues the newsletter — and even that waits as a draft until we send it.

## The one idea to steal: constrain the endpoint, not the prompt

It's tempting to put "never publish anything" in the AI's instructions and move on. That's a wish, not a control — the next model, or a confused run, can ignore it. So we pushed the guarantee down into the endpoint, where it's structural:

```typescript
// status is forced to 'draft' — this endpoint never publishes or emails.
const postData = {
  title, slug, content,
  status: 'draft' as const,
  published_at: null,
}
```

The caller doesn't get to set `status`. There is no code path from this endpoint to a published post or an email. If the token leaked tomorrow, the worst anyone could do is clutter our drafts folder. For an endpoint a CLI tool hits from arbitrary machines, that's the right blast radius — and it's why we could let the agent write completely unsupervised without losing sleep.

## Match the security to that blast radius

Because the damage ceiling is "a junk draft", the auth can be proportionate: a bearer token, compared in constant time. One subtlety worth knowing if you copy this:

```typescript
const a = Buffer.from(provided), b = Buffer.from(expected)
if (a.length !== b.length) return false      // required, not an optimisation
return timingSafeEqual(a, b)
```

`timingSafeEqual` *throws* if the buffers differ in length, so that length check isn't a shortcut — leave it out and a wrong-length token crashes the route instead of being rejected. We also fail closed with a `503` when the server token isn't configured (distinct from a bad token), and return `409` on a slug clash so the caller retries instead of silently overwriting.

## Save before you touch the network

A post generated from a session is unusually expensive to lose — the context that produced it disappears when the session ends. So the publish step writes the file to disk *before* it tries to upload:

```js
writeFileSync(mdPath, header + content + '\n')   // 1) local copy, always
// 2) THEN POST to the ingest endpoint
```

If the token's missing, the network flakes, or the slug clashes, the words are already saved and every error points at the file. It's a one-line reordering that turns "lost the post" into "paste it manually". Worth doing any time a payload is cheap to store and expensive to regenerate.

## Drafts all the way down

The same move repeats at every layer: the endpoint forces a draft, publishing only *drafts* the newsletter, and sending it is a manual click after a final read in a real inbox. Each automation hands off to a person instead of chaining into the next one. With an AI in the author's seat, that's the property that matters — its output is always a *proposal*. It costs us two clicks per post and buys the confidence to let it write on its own.

## What to take away

- **Constrain the endpoint, not the prompt.** "Please don't publish" is vibes; an endpoint that *can't* publish is a guarantee.
- **Prototype the editorial voice with a copy-paste prompt** before building anything — you'll tune the writing ten times and the plumbing once.
- **Write to disk before the network call** when the payload is expensive to recreate.
- **Size the auth to the blast radius** — a draft-only endpoint needs a token and a careful compare, not OAuth.

The benefit isn't really "AI writes our blog". It's that capturing what we learn no longer depends on anyone finding the willpower at 6pm — while a human still decides what the world sees.

