*A build-in-public technical writeup on the mechanics behind Campfyrr's editor — for anyone curious what's actually happening under "the agent writes into the doc live."*
"The agent writes directly into a shared document, live, with every teammate watching" is a simple sentence to say and a genuinely fiddly thing to build correctly. Here's what's actually underneath it.
Every block in a Campfyrr document is contenteditable="false" by default — the whole document is read-only until you deliberately unlock something. There's no formatting toolbar and no floating selection menu. Instead:
- Press Enter on an empty line (or click one) to open a terminal-style
> command prompt. There's only ever one open at a time — click a different spot and it moves there instead of leaving a stray one behind, and it disappears the moment you click away without needing to be explicitly dismissed. - Whatever you type into that prompt is an instruction to the agent, not literal text — it streams straight into the document.
- To hand-edit existing text instead, click a block (or hover it and press
E). The cursor lands exactly where you clicked; Esc or clicking away commits the edit and stamps your name as the author.
The one exception is the document's title, which lives outside this whole block-lock/command-line system entirely — it's plain shared state, not a ProseMirror node, so nothing that manipulates the document's blocks can accidentally create, move, or discard it.
Two sources of truth, deliberately
A live collaborative doc needs to answer "what's true right now" fast, and a persistence layer needs to survive a server restart. Campfyrr keeps these separate on purpose:
- The PartyKit room's Yjs state is the live editing source of truth. It survives reconnects via its own snapshot persistence, and it's what every connected client is actually syncing against in real time.
- Postgres's `content_markdown` column is a periodically-autosaved mirror (a
useAutosave hook, roughly a 4-second debounce) — used for revision archiving, server-rendered title bars, and seeding new rooms. It can lag the live doc by a few seconds, which is fine for those jobs but wrong for anything that needs the *current* on-screen state — sandbox promote, for instance, sends the editor's live JSON directly in its request instead of trusting the DB mirror.
The agent's cursor isn't a WebSocket peer
Every human's cursor is standard Yjs awareness state — ephemeral, tied to a connection, gone when the tab closes. The agent's cursor works differently: it's stored as a Y.Map directly on the shared document. That's a deliberate choice — it means the agent's presence is actual document state that every connected client sees identically, rather than something bolted onto the awareness protocol as an afterthought.
Sandboxes: one restore mechanism, two entry points
Forking a document into a sandbox creates an isolated copy branched from the current point in the doc. An agent can take a real swing at a rewrite there without touching the original. Promoting a sandbox back over its parent, and restoring an old revision from the history drawer, both end up going through the same underlying applyFullReplace path — sandbox-promote just gets there via a ?restore= redirect first. One mechanism, two doors into it, rather than two separate code paths that could drift out of sync with each other.
What isn't built yet
Worth saying plainly, since this is a "what's actually shipped" post, not a roadmap one:
- No real auth/permissions layer. "Zero-auth" here is literal — there are no accounts required to edit, and a document's
visibility field is stored but doesn't gate access yet. Anyone with a doc's URL can edit it today. RevisionSnapshot.yjsUpdate is reserved in the schema but unpopulated — restoring a revision currently works by replacing the ProseMirror document wholesale, not via a true Yjs CRDT merge.- No automated test suite yet.
- One LLM provider (Groq) wired in today; swapping providers is a one-file change, not a rewrite, but it isn't multi-provider out of the box yet.
None of that is hidden from how the product is described — the honest version of "what's built" is the whole point of a post like this one.
--- Synced from Campfyrr: https://ghostcursor-self.vercel.app/doc/3812b544-c (id 3812b544-c) · last synced 2026-09-07T19:23:21.361Z