{
  "version": "https://jsonfeed.org/version/1.1",
  "title": "Dustin Edwards blog: carrel",
  "home_page_url": "https://dustinedwards.info/writing/tags/carrel",
  "feed_url": "https://dustinedwards.info/writing/tags/carrel/feed.json",
  "description": "Posts tagged carrel.",
  "language": "en-US",
  "authors": [
    {
      "name": "Dustin Edwards",
      "url": "https://dustinedwards.info"
    }
  ],
  "items": [
    {
      "id": "https://dustinedwards.info/writing/carrel-ai-drafts-beside-mine",
      "url": "https://dustinedwards.info/writing/carrel-ai-drafts-beside-mine",
      "title": "Carrel, part 3: AI drafts sit beside mine, never on top",
      "content_text": "\n[Part one](/writing/carrel-a-writing-desk-apart-from-the-site) covered why Carrel exists and [part two](/writing/carrel-one-api-for-every-site) covered the small API each site exposes to it. This part covers the question I get most: what is AI allowed to do in there?\n\nThe short answer is that AI can help with almost everything except the two things that make writing mine: deciding what the words are, and deciding when they go public.\n\n## Beside, never on top\n\nCarrel connects to AI assistants through an MCP server. An assistant can list a site's items, read a post, run the checks, render a preview and save a draft. The save is the part that needed the most care.\n\nWhen an assistant saves a draft, Carrel stores it as an AI draft beside my own draft. It never replaces my draft, and it never replaces the text on the site. In the editor I see my version and the AI version side by side, and I decide what, if anything, to take. Every tool description repeats the same line to the assistant, that AI never rewrites my prose unasked, so the rule is in front of the model before its first call rather than discovered after a refusal.\n\nAsking is the switch. If I ask for a draft, the assistant writes one. If I ask for a review, it flags problems instead of fixing them.\n\n## Flags hold the door\n\nCarrel runs checks on save: missing sources for factual claims, continuity slips, broken links, and a list of habits that make prose read as machine-written. A check that finds something raises a flag on the post. A reviewer, human or AI, can raise one too.\n\nAn open flag holds publication. The post cannot go live until I fix the text or dismiss the flag. That is a small rule with a large effect, because it turns \"the AI noticed something\" from a comment I might skim into a gate I have to pass.\n\n## Publishing stays with a person\n\nThe publish tool has three limits:\n\n1. It publishes only on my explicit instruction in the current conversation. An assistant cannot decide that a post is ready.\n2. It publishes what I saved, never the AI draft. No text travels with the publish call, so an assistant cannot slip in a last edit.\n3. It must name the version it expects. If the post changed since the assistant last read it, the publish is refused.\n\nAfter a publish, Carrel records which client published it on my instruction, and emails me a link to unpublish. If something goes out that should not have, undoing it is one click from my inbox.\n\n## This series, as an example\n\nThese three posts were drafted by an AI assistant at my request, as a test of the whole path. The drafts landed beside mine in Carrel, the checks ran, I read and edited them, and I accepted them into my own draft. The publish happened from Carrel on my instruction, through the API from part two, which recorded who did it.\n\nThat is the arrangement I wanted: help with the typing, the checking and the plumbing, and a clear line around the parts that are mine.\n",
      "summary": "How Carrel lets an AI assistant draft, check and preview writing without ever replacing the writer's text or publishing on its own.",
      "date_published": "2026-10-04T00:00:00.000Z",
      "date_modified": "2026-10-04T00:00:00.000Z",
      "tags": [
        "agents",
        "ai",
        "carrel",
        "writing"
      ]
    },
    {
      "id": "https://dustinedwards.info/writing/carrel-one-api-for-every-site",
      "url": "https://dustinedwards.info/writing/carrel-one-api-for-every-site",
      "title": "Carrel, part 2: one small API for every site",
      "content_text": "\n[Part one](/writing/carrel-a-writing-desk-apart-from-the-site) explained why the writing for this site moved into a separate private tool called Carrel. This part covers how Carrel talks to a site, because that contract is the whole design. If it is small and strict, adding a site is an afternoon. If it is large and loose, every site becomes a special case again.\n\n## One package, many adapters\n\nThe contract lives in a small package called site-api. A site installs it and mounts its router under one path. On this site that path is `/api/carrel/v1`. The package handles the shape of the conversation: authentication, request validation, error formats. The site supplies an adapter, a set of functions that answer each request using the code the site already has.\n\nThat split matters. The package is the same everywhere, so Carrel never needs to know which site it is talking to. The adapter is different everywhere, because a podcast site and a personal site store their writing differently. Nothing in the site had to be rewritten to serve Carrel. The adapter wraps the save, publish and render paths that were already there.\n\n## What the contract covers\n\nThe operations are the ones a writer actually uses:\n\n- **List and read.** Carrel asks for the site's items and gets them back with a kind, a status and a version.\n- **Save a draft.** Every save says which version it was based on. If the site's copy has moved since then, the save is refused. Two editors working on one post cannot quietly overwrite each other; the second one finds out.\n- **Preview.** The site returns the full page as it would be served, built by its own renderer. Not the body in a box, the real page with its header, styles and navigation. What I check in Carrel is what readers get.\n- **Publish and unpublish**, through the same publish path the site already used.\n- **Revisions**, read from the history the site already keeps.\n\n## A key that can only do this\n\nCarrel holds one key per site, and that key reaches only the Carrel path. It cannot deploy, change configuration, manage users or touch any other admin route. If it leaked, the damage would be limited to what a writer can do on that one site, and revoking it is one secret change on each side.\n\n## The one rule about first publication\n\nThis site already had a policy that an automated caller may edit and republish a post but may not publish a post for the first time. That decision stays with a person. The adapter keeps that rule and makes one exception: the Carrel key may perform a first publication, because Carrel only publishes on my explicit instruction. Every other key still gets the same refusal it always did, and the site records who published.\n\nThat exception is narrow on purpose. It does not loosen the rule for agents in general; it names one caller that is already gated by a person. How that gate works inside Carrel, and where AI fits in, is [part three](/writing/carrel-ai-drafts-beside-mine).\n",
      "summary": "The contract every site exposes to Carrel: list, read, save with a version check, preview the real page, publish. How this site implements it, and the one rule about first publication.",
      "date_published": "2026-10-04T00:00:00.000Z",
      "date_modified": "2026-10-04T00:00:00.000Z",
      "tags": [
        "api",
        "architecture",
        "carrel",
        "cloudflare-workers"
      ]
    },
    {
      "id": "https://dustinedwards.info/writing/carrel-a-writing-desk-apart-from-the-site",
      "url": "https://dustinedwards.info/writing/carrel-a-writing-desk-apart-from-the-site",
      "title": "Carrel, part 1: a writing desk that lives apart from the site",
      "content_text": "\nA carrel is the small private desk in a library stacks, the one with a shelf, a lamp and a door that does not quite close. I named my writing tool after it because that is the job it does: a quiet place to write that sits next to the collection without being part of it.\n\nThis is the first of three posts on Carrel. This one covers why it exists. The [second](/writing/carrel-one-api-for-every-site) covers the small API every site exposes to it, and the [third](/writing/carrel-ai-drafts-beside-mine) covers how AI fits into the writing without taking it over.\n\n## The problem with an editor inside every site\n\nThis site had its own admin editor, and it worked. The trouble started with the second site. A podcast site has posts too, and so does a product blog, and each one grew its own way to draft, preview and publish. Each editor knew its own site well and nothing else. Moving between them meant remembering which one saved drafts where, which one previewed the real page and which one only showed the body, and which one would let a post go live by accident.\n\nAn admin built into a site is built around that site's data. A writer is not organized that way. I think in pieces of writing, not in databases, and the same week might hold a post here, a show note there and a page of documentation somewhere else.\n\n## One desk, many shelves\n\nCarrel is a separate application at its own address, behind Cloudflare Access, so only I can reach it. It does not store the published writing. Each site keeps its own content and its own history. What Carrel keeps is the work in progress: my drafts, notes, flags from the checks it runs, and drafts that an AI writes when I ask for one.\n\nWhen Carrel opens a site, it reads that site's content through the site's own API and lists everything it finds there. For this site that is more than blog posts. It includes the pages, the CV sections, the phage records, the lab protocols and the publications, each listed as its own kind of item. A post is just one kind.\n\n## The site stays in charge\n\nThe important design choice is what Carrel is not allowed to do. It does not reach into a site's database, and it does not hold a site's deploy keys. Every change goes through the same narrow API, and the site decides whether to accept it. If a save arrives against an old version of a post, the site refuses it. If a key tries to do something it was not given, the site refuses that too.\n\nThat puts the rules where they already lived. I argued for this pattern in an earlier post about [putting the rules in the API, not in the MCP server](/writing/policy-in-the-api-not-the-mcp), and Carrel is the same idea applied to writing. Carrel is one more caller. Delete it and every site still works, still publishes and still enforces its own policy.\n\n## What it costs\n\nA separate tool is one more thing to run, and one more login. The bet is that the cost is paid once, while the cost of several editors is paid every week. So far the bet has held. This series is the test: it was drafted, checked, previewed and published from Carrel, which is what [part two](/writing/carrel-one-api-for-every-site) explains.\n",
      "summary": "Why the writing for this site moved out of the site's own admin and into a separate private tool, and what that separation buys.",
      "date_published": "2026-10-04T00:00:00.000Z",
      "date_modified": "2026-10-04T00:00:00.000Z",
      "tags": [
        "architecture",
        "carrel",
        "cloudflare",
        "writing"
      ]
    }
  ]
}
