The making got 10x easier. The last mile did not.
You can now ask an AI to build a working thing: a dashboard, a small app, a one-pager, a report. It hands you code or a file, and then you hit the dumb wall. The artifact exists, but getting it in front of a specific person means exporting, finding hosting, wiring auth, or just sending a zip that kills the moment.
So I started building the missing verb. Not "generate," which is solved, but "publish this." One sentence to the AI, and the thing it just built becomes a real, hosted, live site behind access control. I build Thryvate, so this is the design I landed on, but the interesting part is not the product, it is what a publish tool actually has to guarantee once an autonomous agent is the one calling it. Full disclosure up front so you can weigh the rest.
Here is what I learned the tool has to do, past the obvious "accept some HTML."
1. Update in place, or you get link rot on every iteration
The naive tool signature is publish(html) -> url. That is wrong the moment the agent iterates. The user says "make the header smaller," the agent regenerates, and if publish mints a fresh URL every time, the link you already shared to a client is now stale.
So the real contract is closer to:
publish(html) -> { site_id, url }
update(site_id, html) -> { url } // same url, new version
The agent has to carry site_id across turns and default to update once a site exists. "Publish this" on turn one and "tweak it" on turn five must land on the same URL. That single decision (stable identity separate from content) removes the most common footgun of agent-driven publishing.
2. Every update is a version, and rollback is one call
Once an agent can overwrite your live site unattended, you need an undo that does not depend on the agent having kept the old output. If turn five produced broken HTML, "ask the AI to regenerate the good version" is not a recovery plan, it is a prayer.
So every update is an append, not a replace, and there is a first-class rollback(site_id, version). The agent is allowed to move fast because a human (or the agent itself) can step back instantly. Treat published versions like git history, not like a single mutable file.
3. The access model is the actual product, not a checkbox
This is the part I underestimated. Paste-to-link hosts (Tiiny, Static, Netlify Drop, and friends, all genuinely good at what they do) give you a public URL: anyone with the link can open it. That is exactly right for a landing page and exactly wrong for the thing your AI built for one client.
When an agent publishes on your behalf, the safe default has to be private, because the agent cannot judge sensitivity. So the tool's access surface is where most of the design went:
set_visibility(site_id, "private" | "public")
set_allowlist(site_id, ["a@co.com", "b@co.com"]) // invite by identity
set_view_password(site_id, "...") // optional second factor
set_link_expiry(site_id, "2026-08-01T00:00:00Z") // auto-dark after
The wedge is verified access, not a secret URL. A private site should make every viewer verify their email before a single byte of the page loads, so "who can see it" is an allowlist you control, not a link that gets forwarded into a group chat. A shared link that anyone can forward is not access control, it is hope.
4. Render it in a sandbox, because the AI wrote it
You are hosting arbitrary AI-generated HTML and JS on your domain. Serve it from the same origin as your app and one generated fetch or cookie read can reach places it should not.
The fix is boring and non-negotiable: sandboxed, cookieless origin per site. Each published page runs isolated from your session and from every other site. When the author is a language model, "assume the payload is hostile" is just the correct default, even when the author had good intentions.
5. Give the published page a data backend, or it is only ever a brochure
The thing that surprised me most: as soon as the AI can publish a live page, people immediately want that page to do something. Collect an RSVP. Record a high score. Take a form submission. A static file cannot, and asking a non-technical user to "stand up a backend" puts the wall right back.
So the published page ships with a tiny global SDK, no keys, no setup:
// inside the AI-generated page
await window.THRYVATE.append("rsvps", { name, email });
const { records } = await window.THRYVATE.query("rsvps", { order: "desc", limit: 50 });
Now the AI can build a working guestbook, a poll, a signup, or a leaderboard, and it just persists. The publish tool is not shipping a document, it is shipping a small application with somewhere to put its data.
The tool surface, in one place
Pulling it together, a "publish this" tool an agent can safely drive ends up being roughly:
-
publish/updatewith stablesite_id(no link rot) - versioning +
rollback(fearless iteration) -
set_visibility/set_allowlist/set_view_password/set_link_expiry(private by default, verified access) - sandboxed per-site origin (untrusted author)
- an in-page data SDK (published pages that actually do things)
None of these are exotic on their own. What is new is that the caller is an autonomous agent, so the defaults have to be safe without a human in the loop for each step: private not public, versioned not overwritten, sandboxed not trusted.
If you are wiring your own agent to ship things to the web, I think those five are the load-bearing pieces, whatever you build it on. And if you would rather not build the plumbing, that is the whole reason Thryvate exists: your AI publishes what it made straight to a hosted, private-by-default site, and you control who gets in. Happy to answer anything about the access model in the comments.
Top comments (2)
The update-in-place requirement is the part people miss. “publish(html) -> url” looks clean, but real agent workflows iterate. Without stable identity for the published artifact, every revision creates operational drift.
A publish tool is where agent design stops being theoretical. The tool has to verify exact text, target account, attachment state, final permalink, and rollback path before "publish" is a safe verb.