shiichan

Agent Browsers Can Now Preview Dev Servers Without Public URLs!

Hi, it's Shii-chan!

Cloudflare Changelog developers.cloudflare.com

When an agent builds a website, it needs to check its work in a real browser. Today's news makes that a lot safer, so let's dive in!

What was announced?

In the Cloudflare Changelog, internal hostnames were added to the browser in the Agents SDK!

When you give the browser a hostname such as preview.internal, requests to it go to your agent instead of the Internet. You can preview a dev server running in a container without exposing it publicly.

These are the things you can preview:

  • A dev server running in a sandbox
  • A folder in the agent's workspace
  • Another Worker

The story so far

To check a site your agent built in a real browser, you generally had to make the dev server reachable somehow. You only wanted to check your work, but you ended up needing a public URL.

What changes

No public URL is needed, so your preview server stays off the public Internet! The model can look at a sandbox dev server in the same browser it uses for the web.

This is a browser capability, independent of your agent harness. There are built-in adapters for Pi Durable, AI SDK, and TanStack AI, and other harnesses can connect by implementing a tool with browser.session().

Dive Deep

Here are the key points:

  • Each internal host has a fetch function that runs in your Durable Object, so it can use the object's bindings and state
  • A host can send requests straight to another Worker through a service binding
  • Add a target schema to a host and the model chooses what it serves, such as a port or folder
  • Stored targets persist across turns, browser restarts, and redeploys

You need Agents SDK v0.31.0 or later. If you already use browserTool, check the v0.31.0 upgrade notes. browserTool now runs model code in its own Dynamic Worker and no longer needs a CodemodeRuntime export.

Here's a condensed sketch of the setup from the original example:

browser = new Browser({
  provider: browserRun(this.env.BROWSER, {
    internalHosts: {
      router: this.ctx.exports.InternalHostRouter,
      hosts: {
        "preview.internal": {
          description: "Your dev servers",
          target: z.object({ port: z.number().int().min(1024) }),
          fetch: (request, { port }) => fetchSandboxPort(port, request),
        },
      },
    },
  }),
});

On the model side, it assigns a target to the host and opens the returned URL:

const { url } = await browser.assignHost({
  host: "preview.internal",
  target: { port: 5173 },
});

Each later request to http://preview.internal/ calls the host's fetch with { port: 5173 }. The port minimum of 1024 is a schema choice in the sample, not a platform limit. Limits and security guidance are in the official docs, so take a look before deploying!

Wrap-up

  • Browser internal hostnames let you preview dev servers and more without public URLs
  • Each host's fetch runs in a Durable Object with access to bindings and state
  • A target schema lets the model choose what gets served
  • It's harness-independent and needs Agents SDK v0.31.0 or later

This one is great for anyone running site-building agents on Cloudflare!