Publish HTML files as shareable links — and optionally collect inline feedback on them and synthesize it with AI.
htmldrop has three modes:
- Simple share — publish any HTML file and get a public (or password-protected) link, hosted free on Surge.sh.
- Collaborative feedback + converge — publish with an embedded annotation widget so reviewers can highlight text and comment (no account needed), then pull that feedback and let an LLM synthesize an improved version.
- Edit mode — a local, real-time loop to refine a page with your AI agent before you publish it. No hosting, no link; it runs entirely on
127.0.0.1.
npm install -g @yeefeiooi/htmldrop
(The installed command is htmldrop. Requires Node.js ≥ 18.)
- No account needed
- Setup
- Command reference
- Example walkthroughs
- The agent loop
- Edit mode (local, pre-publish)
- Multi-provider AI (Anthropic / OpenAI / Gemini)
- Architecture
- Security model
- Self-hosting the feedback backend
- Config
There's no htmldrop signup, login, or hosted dashboard — the CLI is the whole product. Going from zero to a shareable link is only:
npm install -g @yeefeiooi/htmldrophtmldrop init— logs into Surge (a free third-party static host; the only account involved, and it isn't ours) and picks your subdomain.htmldrop auth setup— generates a local random author key on your machine (no email, no verification, no server call). Only needed for the feedback/converge features.
Then htmldrop push file.html --feedback and share the link. By default, comments are stored on our free shared Worker (auto-expiring after 90 days). If you'd rather they live on infrastructure you control, self-host the Worker — still no signup, just one env var (HTMLDROP_WORKER_URL).
Two more things are optional and bring-your-own — htmldrop stores neither:
- AI key — only for
converge/ AI insights; billed by your own provider, used for a single request. See Multi-provider AI. - Doc password — only for private shares; used to encrypt in memory, then discarded. See Security model.
# 1. Install the CLI
npm install -g @yeefeiooi/htmldrop
# 2. One-time: log in to Surge and pick a subdomain (for simple share)
htmldrop init
# 3. One-time: generate an author key (only needed for feedback/converge)
htmldrop auth setupinit runs surge login and saves your subdomain to ~/.htmldrop/config.json.
auth setup generates an author API key (also stored there) that identifies you as the document owner when pulling, clearing, or converging feedback.
For AI converge you also need an LLM API key in your environment — see Multi-provider AI.
| Command | What it does |
|---|---|
htmldrop init |
One-time setup: Surge login + subdomain |
htmldrop auth setup [--force] |
Generate the author API key (--force regenerates) |
htmldrop push <file> |
Publish a file. Flags: --password <p>, --noindex, --open |
htmldrop push <file> --feedback |
Publish with the annotation widget. --new-doc forces a fresh link. Combine with --password for a feedback-enabled private doc |
htmldrop list |
List published files with their URLs |
htmldrop open <file> |
Open a published file in the browser |
htmldrop delete <file> |
Remove a file and redeploy |
htmldrop feedback pull <file> [--json] [--save] |
Retrieve feedback for your own file. --save writes it to <file>.feedback.json in your repo (owned + versioned) |
htmldrop feedback read <docId|url> [--json] |
Read feedback for any doc by id or link — no ownership, no manifest (for teammates/agents) |
htmldrop feedback list |
List which files have feedback enabled |
htmldrop feedback add [file] --text <t> |
Post a comment. Use --doc-id <id|url> to comment on a doc you didn't publish. --name <n>, --on <anchor>, --parent-id <id> |
htmldrop feedback clear <file> |
Delete all feedback (owner only) |
htmldrop fetch <url> [--password <p>] [--out <f>] |
Fetch a published doc, decrypting password-protected pages — lets an agent read the content |
htmldrop converge <file> |
Synthesize feedback → improved HTML (owner). --dry-run, --provider, --model, --api-key |
htmldrop studio |
Open the Converge Studio dashboard locally. --port <n>, --no-browser |
htmldrop edit start <file> |
Edit mode — serve the file locally and iterate with your agent in real time before publishing. --with-feedback loads the published doc's reviewer comments; --no-open skips the browser |
htmldrop edit poll <file> [--json] |
The agent listens — blocks until you leave a comment (or answer a question), then returns it with the page's comments and layout warnings as context |
htmldrop edit reply <file> --text <t> |
The agent replies into the conversation after editing the file |
htmldrop edit ask <file> --text <q> [--options "A|B|C"] |
Ask the author a question in the browser; their answer returns on the next poll as {choice, text} |
htmldrop edit layout <file> [--json] |
Report layout issues (overflow, clipped/overlapping text) in the rendered page |
htmldrop edit end <file> · htmldrop edit stop |
End a session · shut the local edit server down |
File arguments accept an absolute path, a relative path, or the bare filename — they all resolve to the same published file. Commands that act on your own doc (pull, clear, converge) use the local manifest + author key; the teammate-facing commands (read, add --doc-id, fetch) work from just a link.
Each example is a self-contained sequence of steps you can copy.
htmldrop push report.html
# → Published: https://your-subdomain.surge.sh/report.htmlAdd --noindex to block search engines/AI crawlers, or --open to open it after deploy.
htmldrop push private-spec.html --password coral-sunset-42
# → Published (AES-256-GCM encrypted). Share BOTH the URL and the password.The content is encrypted client-side; viewers must enter the password to decrypt it. htmldrop stores the password nowhere — save it yourself, because a forgotten password can't be recovered (just re-push with a new one). See Security model.
This is the full feedback → converge loop, broken into steps.
C1. Publish with feedback enabled
htmldrop push spec.html --feedback
# → Feedback URL: https://htmldrop-feedback.htmldrop.workers.dev/doc/<uuid>That single URL is for everyone — reviewers and you.
C2. Reviewers comment (in the browser)
Share the Feedback URL. A reviewer opens it, selects text, clicks the “+ Comment” tooltip, and leaves a comment anchored to that text — no account, no login. They can also: drag a box over an area (the ▢ toggle in the panel) to comment on a region — images, layouts, anything that isn't a clean text run; leave page-level comments; and reply to others. New comments appear via the panel’s refresh button.
C3. Read the feedback from the CLI
htmldrop feedback pull spec.html
# Lists every comment with author, anchor text, and timestamp.
htmldrop feedback pull spec.html --json # machine-readableC4. Add an evidence-backed comment (optional)
You — or an AI agent acting for you — can add a comment, optionally anchored to specific text:
htmldrop feedback add spec.html \
--text "Benchmarks show PostgreSQL ~3x the write throughput here." \
--name "AI Research" \
--on "PostgreSQL"C5. Converge the feedback into an improved version
# Preview the prompt without calling the model:
htmldrop converge spec.html --dry-run
# Real run (needs an LLM API key — see Multi-provider AI):
export ANTHROPIC_API_KEY=sk-ant-...
htmldrop converge spec.html
# → Writes spec.converged.html incorporating the feedback.C6. Re-publish to the same link
htmldrop push spec.converged.html --feedback # or re-push spec.html after editingRe-pushing a file that already has feedback reuses the same docId, so the link stays stable and existing comments stay attached. Use --new-doc only when you want a clean slate.
htmldrop studioOpens Converge Studio locally: the document on the left, comments grouped into segments on the right, with debate detection and per-segment AI insights/converge actions.
htmldrop is designed so an AI coding agent (e.g. Claude Code) can drive the whole review cycle on your behalf:
Agent generates a doc → htmldrop push spec.html --feedback → shares the link
│
reviewers comment on the link
│
Agent: htmldrop feedback pull spec.html → reads the feedback
Agent: htmldrop feedback add … --on "…" → injects researched, anchored context
Agent: htmldrop converge spec.html → synthesizes an improved version
Agent: htmldrop push spec.html --feedback → updates the SAME link, comments intact
Because re-push keeps the same URL, the document can iterate in place while reviewers keep using the link they already have.
push --feedback is for asynchronous review — you ship a link and reviewers comment over time. Edit mode is the opposite: a local, real-time loop where you sit with your agent and refine the page first. Nothing is published; it runs entirely on 127.0.0.1.
htmldrop edit start report.html # serve locally + open the browser
htmldrop edit start report.html --with-feedback # …and load the published doc's reviewer commentsOne surface. The page shows your document with the annotation widget plus a small control bar. Select text (the comment box auto-opens) or drag an area to comment; ⌘⏎ / Ctrl+Enter sends. A page-level comment is a message to the agent; a threaded reply is the agent's answer. The page live-reloads as the agent edits, with comments re-anchored.
Live ⇄ Async. The control bar toggles the mode:
- Live — an agent poll is attached; your comments reach it in real time.
- Async — comments are collected for a later pull; nothing is sent until you click Send N to agent.
The agent's side of the loop:
You (browser): comment "tighten the intro" on the highlighted line
Agent: htmldrop edit poll report.html --json # blocks until your comment arrives
Agent: …edits report.html…
Agent: htmldrop edit reply report.html --text "done — tied it to a number"
→ page live-reloads · the reply shows in the control bar · repeat
The agent can also ask you a question mid-loop — htmldrop edit ask report.html --text "iOS-first or Android-first?" --options "iOS|Android|Both" pops a card in the browser; your click/answer returns on its next poll.
- Author-facing & separate. Sessions live under
~/.htmldrop/edit/, keyed by file path — never mixed into a published thread. Edit mode is for you to firm the doc up with an agent; when ready,htmldrop push --feedbackshares it for external review as usual. - Reliable. A stable local port keeps an open tab alive across restarts; a failed save shows a retry toast (and keeps your text) rather than losing it; sessions persist on disk.
The local server binds loopback only, rejects non-loopback Host/Origin (guarding against DNS-rebinding and CSRF from other sites you have open), serves only .html/.htm, and self-shuts after 30 minutes idle.
htmldrop has two implicit roles, enforced by the architecture rather than an account system:
| Role | Who | Can do | How |
|---|---|---|---|
| Reviewer | anyone with the link (your teammate, or their agent) | read the doc, read comments, add comments & replies | public Worker endpoints — no key needed |
| Owner | whoever holds the author key that registered the doc | everything above + converge, feedback clear |
author key in ~/.htmldrop/config.json |
A teammate (or their Claude Code / Codex session) reviews without owning anything. You share the link — and the password if the doc is encrypted. They:
# Read the document (decrypts a password-protected page so the agent can analyze it)
htmldrop fetch https://you.surge.sh/spec.html --password coral-sunset-42
# Read all reviewer comments — by link or docId, no ownership
htmldrop feedback read https://htmldrop-feedback.htmldrop.workers.dev/doc/<id>
# Add their own comment, optionally anchored to text
htmldrop feedback add --doc-id <id|url> --text "Consider X here" --on "the exact phrase" --name "Alex"It’s symmetric: when your teammate publishes their doc, they’re the owner and you’re the reviewer.
Only the owner converges. converge and clear require the author key, so synthesizing/rewriting a doc stays with whoever published it — teammates contribute feedback, the owner decides when to converge. This is the role boundary you want today, with no extra setup.
No — and it isn't needed today. A teammate's Claude/Codex session already accesses a doc with just the link (and the password, for private docs): fetch --password decrypts the content, feedback read / feedback add --doc-id handle comments. The password is the access mechanism; no login/identity standard is required.
What auth.md is — and is NOT. auth.md is a standard a service adopts (like "Sign in with Google" / OIDC), so both sides must speak it. It is not a password manager, a form-filler, or a way to log into arbitrary existing username/password websites. Our own password gate (client-side AES) is unrelated to auth.md. The spec defines two flows:
- Agent-verified (ID-JAG) — an agent provider (Anthropic/OpenAI/…) signs an assertion vouching "my agent acts for verified-user Jane." LLM/agent-specific. We expose the service side (
/.well-known/oauth-authorization-server,/agent/auth), but no provider mints ID-JAGs for arbitrary services yet, so it isn't usable end-to-end. - User-claimed (OTP) — a human proves identity via a one-time email/SMS code. Passwordless login for humans; htmldrop does not implement this today.
When you'd actually adopt more of auth.md: only when you want identity-based access (allowlist jane@example.com, revoke per person, audit who opened it) instead of a shared password — i.e., real multi-user roles. For a trusted team sharing a link + password out-of-band, the current model is sufficient and requires no auth.md adoption. Identity-based content access also implies the server can decrypt your doc (see the Security model note) — a deliberate trade-off, not a free upgrade.
You only need an LLM API key for the AI features (
htmldrop convergeand the dashboard’s AI insights). Everything else works without one.
Without an AI key With an AI key Publish, password-protect, list,delete; enable--feedback; collect,pull,list,add, reply to, andclearcomments; view everything in Converge StudioAll of the above plus converge(AI synthesis into an improved doc) and per-segment AI research insightsIf you don’t set a key, the AI commands simply tell you one is required and stop — nothing else is affected. If you do set one, it unlocks converge + insights.
- Your key, your cost. It’s bring-your-own-key: you pay your provider (Anthropic / OpenAI / Gemini) directly at their published rates. htmldrop adds no markup and ships no key of its own.
- We never store your key. The CLI reads it from your terminal environment (or
--api-key) for that single run only. The browser dashboard holds it in session memory and wipes it when you close the tab. The Worker uses it for one request and forgets it — it is never written to disk or KV.- Handle it securely — it lives in your terminal. Keep it in an environment variable, don’t commit it to git, don’t paste it into shared logs or chats, and rotate it at your provider if it’s ever exposed.
converge and the dashboard’s AI insights work with Anthropic, OpenAI, or Gemini. The provider is auto-detected from your API key’s prefix, and you can override it.
Key resolution (CLI): --api-key, else LLM_API_KEY, ANTHROPIC_API_KEY, OPENAI_API_KEY, or GEMINI_API_KEY.
Auto-detection by prefix:
| Prefix | Provider |
|---|---|
sk-ant-… |
Anthropic |
sk-… |
OpenAI |
AIza… |
Gemini |
Default models (override with --model or the dashboard’s model field):
| Provider | Default model | Approx. cost (in / out per 1M) |
|---|---|---|
| Anthropic | claude-sonnet-4-6 |
~$3 / ~$15 |
| OpenAI | gpt-5.4-mini |
$0.75 / $4.50 |
| Gemini | gemini-3.1-flash-lite |
$0.25 / $1.50 |
# Auto-detected from the key:
ANTHROPIC_API_KEY=sk-ant-... htmldrop converge spec.html
# Explicit override (e.g. flagship OpenAI for top quality):
htmldrop converge spec.html --provider openai --model gpt-5.4 --api-key sk-...No SDKs are required — htmldrop calls each provider’s HTTP API directly.
Simple share → files are staged in ~/.htmldrop/site/, an index gallery is generated, and the whole directory is deployed to your Surge subdomain.
Feedback → a Cloudflare Worker (htmldrop-feedback.htmldrop.workers.dev) stores comments in KV and serves the document at one URL:
GET /doc/:docId— serves your HTML with the annotation widget injected (same URL for reviewers and author)POST /api/feedback/:docId— anonymous comment submission (rate-limited per IP + per doc)GET /api/feedback/:docId— public read (with?since=for incremental fetch)POST /api/feedback/:docId/:commentId/reply— threaded repliesPOST/GET /api/insights/:docId,POST /api/converge/:docId— AI insights/synthesis (bring-your-own-key).well-known/oauth-authorization-server+/agent/auth— auth.md discovery + agent registration, so future cloud-hosted agents can authenticate via ID-JAG (the local author key continues to work today)
Documents and feedback auto-expire after 90 days of inactivity.
- No reviewer accounts. Reviewers comment anonymously; abuse is bounded by per-IP and per-doc rate limits.
- Author key lives only in
~/.htmldrop/config.jsonon your machine. - LLM API key (bring-your-own): in the dashboard it’s held in
sessionStorageand cleared when you close the browser — never persisted to disk or stored on the server. The Worker uses it for the single request and forgets it. - Password-protected shares use AES-256-GCM, with the key derived from your password via PBKDF2 (SHA-256, 600k iterations) and a random salt/IV — authenticated encryption, computed client-side (the browser decrypts with built-in WebCrypto; the password is never sent to a server). Only the encrypted blob is uploaded (to Surge), so the plaintext never reaches Surge or the Worker.
- Your password is stored nowhere. It's held in memory just long enough to encrypt the file at push time, then discarded — never written to
~/.htmldrop/config.json(which holds onlysubdomain,email,authorKey), never uploaded, never sent to any server. Two consequences: (1) no breach of our infrastructure can expose a private doc, and (2) a forgotten password can't be recovered — there's nothing to recover it from; just re-push with a new one. You (or your agent) choose the password and are responsible for saving it (a password manager is ideal). - Keep the password out of shell history: use a bare
--passwordflag to read from theHTMLDROP_PASSWORDenv var, or be prompted with hidden input — instead of--password <pw>on the command line. Applies to bothpushandfetch. - Caveat — comments are not encrypted. A password protects the document content (on Surge), but comments are stored in plaintext on the Worker and readable by anyone who has the docId. For a private doc, comment confidentiality rests on the unguessable link, not the password.
Two pieces: Surge hosts your HTML (stores nothing); a Cloudflare Worker + KV stores the comments (the only part that needs a server — a static page can't accept writes). You choose whose Worker.
- Zero setup —
htmldrop push --feedbackjust works. - Comments live in our Cloudflare Worker KV. Honestly: we can technically read them — but the code is open-source, we never use your data, comments auto-expire after 90 days, and you can wipe them anytime with
htmldrop feedback clear. - Not zero-knowledge. For that, self-host ↓.
FREE (shared):
you ─push────────► Surge (your HTML)
reviewer ─comment─► OUR Worker + KV (auto-expires 90d · clearable)
you ─feedback pull --save─► comments.json in YOUR repo
- Deploy the Worker to your own free Cloudflare account → comments never touch our infrastructure. We genuinely see nothing.
- Setup (one time):
cd worker npx wrangler kv namespace create FEEDBACK npx wrangler kv namespace create RATE_LIMITS npx wrangler kv namespace create AUTHORS # paste the printed IDs into wrangler.toml, then: npx wrangler deploy
- Point the CLI at it (set once — applies to push, feedback, converge, studio):
Published docs' widgets then talk to your Worker.
export HTMLDROP_WORKER_URL=https://your-worker.your-subdomain.workers.dev
OWN-YOUR-DATA (self-host):
you ─push────────► Surge (your HTML)
reviewer ─comment─► YOUR Worker + KV (your account — we never see it)
you ─feedback pull --save─► comments.json in YOUR repo
htmldrop feedback pull <file> --savewrites comments to<file>.feedback.jsonin your repo — owned, versioned, private. The Worker is just the live inbox; your repo is the system of record.- Reviewers never need repo access — you pull + commit. (A teammate with no push rights still comments fine in the browser; you sync it into the repo.)
Stored at ~/.htmldrop/config.json:
{
"subdomain": "your-subdomain",
"email": "you@example.com",
"authorKey": "…"
}MIT