Publish AI-agent plans to stable, versioned review URLs — HTML and Markdown, one command.
Open source by Tapetide · MIT licensed · runs on Cloudflare's free tier
Quick start · How it works · CLI · API · Security model · Self-hosting
You are not running one coding agent any more. You are running a fleet.
One is on your laptop. One is on a remote box you SSH into. Two more run in CI. A long-running one lives on a cloud VM you spun up last week. Every one of them produces work you actually need to read — a migration plan, an architecture proposal, a security audit, a refactor report.
And every one of them dumps that work somewhere you can't get to it:
- a
plan.mdsitting in/tmpon a machine you'd have to SSH into - a wall of Markdown flattened into a chat window, asterisks and all
- a CI artifact that expires in seven days
- a file on a container that no longer exists
So you end up reading raw Markdown in a terminal, or copy-pasting into a gist, or asking the agent to summarise the thing it already wrote. The plan was fine. Getting to it was the problem.
agentdraft gives every agent, on every machine, one command that returns a URL a human
can open in a browser:
agentdraft upload plan.md
# → https://agentdraft.tapetide.com/d/a1b2c3d4e5f6Markdown is rendered into a readable document. HTML is served byte-for-byte. Every re-upload of the same file is a new immutable version, and the old ones stay live. One place for every agent's output, wherever it ran.
Install the CLI (single Go binary, no runtime):
curl -fsSL https://raw.githubusercontent.com/Tapetide-hq/agentdraft/main/cli/install.sh | shAuthenticate once per machine, then publish:
agentdraft auth set ad_your_key # create a key in the dashboard
agentdraft upload plan.md # Markdown → rendered for reading
agentdraft upload report.html # HTML → served byte-for-byte
agentdraft list # everything you've published
agentdraft fetch <url> -o plan.md # another agent reads it backPoint your agent at that command and you are done. There is no build step, no framework, and nothing to deploy per document.
Go CLI ───HTTPS──▶ api.agentdraft.tapetide.com (API, authenticated)
Dashboard ───────▶ │ │
│ │
┌─────▼───┐ ┌────▼──┐
│ D1 │ │ R2 │
│metadata │ │ blobs │
└─────▲───┘ └────▲──┘
│ │
Browser ─────────▶ agentdraft.tapetide.com (documents, NO cookies)
| Component | Tech | Role |
|---|---|---|
worker/ |
Hono + TypeScript on Cloudflare Workers | API, auth, validation, Markdown rendering |
content-worker/ |
TypeScript on Workers | Public document serving, isolated origin |
dashboard/ |
SvelteKit on Workers | Projects, version history, API keys |
cli/ |
Go + cobra | upload, fetch, list, auth |
| D1 / R2 / KV | Cloudflare | metadata / document blobs / rate limits |
Send either. A .md file is rendered once at upload into a self-contained HTML
document — served at /d/:id so a human reads formatted prose, while the exact source
bytes stay retrievable at /d/:id/raw as text/markdown. Rendering at request time
would let a renderer upgrade silently change an already-published document, so it doesn't
happen.
Every upload to the same draft allocates a new version number atomically. /d/:id serves
the latest; /d/:id/v/3 serves version 3 forever, cached for a year. Identical bytes are
deduplicated — the same content re-uploaded stores no second copy but still records a new
version, so history and git metadata stay accurate.
The CLI records which draft a local file maps to, so re-running upload on the same file
updates the same URL instead of littering new ones. Git branch, commit SHA and dirty
state are captured automatically when you publish from inside a repository.
Uploaded documents are treated as hostile, because they are:
- One authoritative validator. A real HTML5 parser (parse5) rejects
<script>,<iframe>,<form>, event handlers,javascript:URLs, mXSS namespace tricks and<template>/<noscript>parse-differential smuggling — enforced against a shared ~60-case conformance corpus. The CLI does fast transport pre-checks only; two independent validators would drift, one authority cannot. - Markdown rendering is not sanitising. Markdown permits raw HTML passthrough, so
rendered output goes through the same validator. A
.mdfile containing<script>is rejected. - Separate origin, always. Documents are served from an origin that never holds a session cookie, so a CSP bypass in a published page cannot reach a dashboard session.
- Invite-only publishing. Scoped, revocable API keys; no anonymous uploads by design, so this cannot become a free phishing host. Reading is anonymous.
Full threat model: SECURITY.md.
Self-hosting takes about ten minutes and costs $0/month at hobby scale (Workers, D1, R2 and KV free tiers). Full walkthrough: docs/self-hosting.md.
git clone https://github.com/Tapetide-hq/agentdraft.git && cd agentdraft
wrangler d1 create agentdraft-db
wrangler r2 bucket create agentdraft-html
wrangler kv namespace create agentdraft-ratelimit
# put the ids in worker/wrangler.jsonc, then:
cd worker && bun install && wrangler d1 migrations apply agentdraft-db --remote && bun run deploycd worker && bun run test # 60-case HTML policy conformance corpus
cd cli && go test ./... # CLI unit tests
# adversarial end-to-end suite, against a real deployment
API=https://api.agentdraft.tapetide.com \
CONTENT=https://agentdraft.tapetide.com \
KEY=ad_your_key ./tests/brutal.shtests/brutal.sh runs 198 assertions: XSS and policy-bypass attempts, auth bypass,
privilege escalation, IDOR, malformed input, injection, size limits, versioning, dedup,
idempotency, conditional requests on cold and warm cache, Markdown rendering and
hostile-Markdown rejection, cache-leak-after-delete, and concurrent-upload races. It needs
~80 uploads against a 100/hour limit, so use a fresh key per run.
| Doc | Contents |
|---|---|
| docs/cli.md | Every command and flag |
| docs/api.md | HTTP API reference |
| docs/architecture.md | Design decisions and request flows |
| docs/self-hosting.md | Deploy your own instance |
| docs/openapi.yaml | Machine-readable API spec |
| SECURITY.md | Threat model, validation policy, reporting |
| CONTRIBUTING.md | Dev setup and PR guidelines |
Is this web hosting? No. It publishes agent-generated documents for review. There is no domain, DNS, database or server for you to manage per document.
Can I use it for a static site? It's not built for that. Documents are noindex by
design and every page is treated as untrusted content.
Are published URLs private? They are unguessable but public — anyone with the link can read it, and links are not search-indexed. Treat them as shareable, not secret.
Does it work offline / self-hosted? Yes, that's the intended deployment. Bring your own Cloudflare account.