Skip to content
All posts
3 min read

Drafts are a security boundary

The blog's machine channel can create drafts and nothing else: published slugs 409, and publishing does not exist in its vocabulary. Capability design beats instructions.

AISystems

Yesterday this site grew an API that lets a machine write into my blog. Designing it, the question that mattered was not what the writer should do. It was what the channel should make impossible.

The endpoint takes a bearer token and creates or updates drafts. That is its entire vocabulary. There is no publish parameter to misuse, no status field it accepts, no code path from that route to a live page. A draft lands in my admin wearing a badge, a Discord ping tells me it exists, and the only way it ever reaches a reader is my click in the editor. The route also refuses history: a slug that is already published answers 409, so the machine cannot overwrite the archive any more than it can extend the front page. And it answers 422 to any em or en dash in the copy, which sounds petty until you have read enough machine prose to know the dash is the tell.

The distinction underneath: instructions are policy, capabilities are physics. The scheduled writer's prompt does say drafts only, and I keep it there for clarity, but a prompt is the weakest wall in the building. Models have bad days. Prompts meet injected text. The model behind the routine will be swapped, someday, for one I have not met. So the security model assumes the writer is compromised and asks what the blast radius is. The answer, by construction: a strange draft and a notification. I can live with that answer forever, under any model, which is what a boundary is for.

If a system prompt is your security model, you do not have one.

The channel's read side is shaped with the same intent. A GET on the endpoint lists pending drafts, so the routine can check its own inbox and skip topics it already filed instead of duplicating work. Lay the tiers side by side and the design states itself: reading is free, writing is shaped, publishing is absent. Even the credential follows the shape: the channel runs on its own bearer token, unrelated to the admin session, so the worst a leaked token yields is draft graffiti and a rotation. A secret scoped to a narrow capability is cheap to lose, which is the only kind of secret worth keeping near an agent.

The unexpected benefit is what the hard wall buys everywhere else: generosity. Because publishing is physically off the table, I do not have to hedge the writer's freedom. It reads the repo, picks its own topic off a queue, writes with full latitude, files the result. Systems with fuzzy boundaries end up nervous everywhere, sprinkling be-careful into every instruction. One wall you can lean on beats ten you have to keep repainting.

The boundary also holds in the other direction, which I did not plan and now like best. The LinkedIn composer in the admin refuses to share a draft even when I am the one clicking. Until a post is published, neither the machine nor a careless owner can broadcast it. The gate does not distrust the AI specifically. It distrusts everyone equally, which is what makes it a boundary instead of a leash.

The routine has run once so far, and its first draft is still sitting in the inbox, waiting on my click. That pause is not friction. That pause is the entire design, working.

Remiel Shirazi

CTO & Co-founder | AI, Roblox & Piano