# Web automation

## What it does

Web automation lets Speedwave drive a real browser: open a page, click and type, fill in forms, take screenshots, read the rendered DOM, and check that elements or text are visible. Use it when a plain web fetch is not enough, for a JavaScript-rendered app, a single-page app, or a page that needs time to load. See [Automated browser testing](/docs/features/browser-testing/) for how this plays out inside a session.

It also extends what a site audit can check. Of the site audit's rules, 115 run through a plain web fetch with no browser needed; the other 13 need a live browser and are skipped when web automation is off.

## What it consists of

Speedwave provides this through Microsoft's official `@playwright/mcp` server, which runs a headless Chromium browser inside a hardened worker container. There are no credentials and no token file: the worker runs as the compose service `mcp-playwright`, in a container named `speedwave_<project>_mcp_playwright`.

The browser is a shared service rather than a per-plugin one, so Chromium is not duplicated when more than one plugin needs it. Speedwave reaches it through the same MCP tools whether you ask directly or a plugin calls it on your behalf.

## How it works

Because there are no credentials to store, there is nothing to configure. Enabling web automation for a project is enough to make it ready. Speedwave runs the `@playwright/mcp` server with its default tool set, no extra flags, which exposes 21 tools:

| Tool | What it does |
| --- | --- |
| `browser_navigate` | Loads a URL in the page. |
| `browser_navigate_back` | Goes back one step in the page history. |
| `browser_tabs` | Lists, opens, closes, or switches between browser tabs. |
| `browser_snapshot` | Captures an accessibility snapshot of the current page. Read only. |
| `browser_take_screenshot` | Takes a screenshot of the viewport or the full page. Read only. |
| `browser_console_messages` | Reads the browser's console log. Read only. |
| `browser_network_requests` | Lists network requests made since the page loaded. Read only. |
| `browser_evaluate` | Runs a JavaScript expression against the page or an element. |
| `browser_run_code` | Runs a short Playwright code snippet against the page. |
| `browser_click` | Clicks an element. |
| `browser_hover` | Moves the pointer over an element. |
| `browser_drag` | Drags one element and drops it on another. |
| `browser_type` | Types text into an editable element. |
| `browser_press_key` | Presses a single key or key combination. |
| `browser_select_option` | Chooses an option in a dropdown. |
| `browser_fill_form` | Fills several form fields in one call. |
| `browser_file_upload` | Attaches one or more files to a file input. |
| `browser_handle_dialog` | Accepts or dismisses a browser dialog such as an alert or a confirm box. |
| `browser_wait_for` | Waits for text to appear, disappear, or a fixed time to pass. |
| `browser_resize` | Resizes the browser window. |
| `browser_close` | Closes the current page. |

None of these tools are destructive in the way a delete call on a real data store is: the browser's state is thrown away on every container restart anyway. Screenshots and page dumps come back to Speedwave as inline base64 data in the tool response. Web automation does not write any files into your project.

The browser runs in a container, so `localhost` inside it means the container, not your machine. To reach a dev server running on your computer, use `host.docker.internal` in place of `localhost`, for example `http://host.docker.internal:4200` for an app on port 4200. Speedwave maps that alias to your host's gateway address, so it can load and test a site you are running locally.

Each container restart wipes the browser's temporary storage, giving a clean profile every time. Cookies and other storage do not carry over between runs, so a login from one session is not available in the next.
**Caution:** Because web automation can reach your machine through `host.docker.internal`, it can also reach any service you run on `127.0.0.1` with no authentication of its own, such as a passwordless Postgres, a Redis without `requirepass`, or a loopback dev server. It can connect to those the same way it connects to your dev server.

## Set it up

1. Turn on [**Beta features**](/docs/guides/beta-features/) in the Speedwave tray menu, since web automation is a beta integration.
2. Enable it for your project. There is nothing to authenticate.
3. Wait for the containers to restart. The first time you enable it, Speedwave builds the worker image and pulls Chromium, which extends that wait; later toggles reuse the cached image and are near-instant.

<DesktopFrame screen="integrations" />

See [Connect an integration](/docs/guides/connect-an-integration/) for the pattern every integration follows.

## Security boundaries

The worker gets the same hardening as every other container: every Linux capability dropped, no privilege escalation, a read-only root filesystem, and `noexec,nosuid` on temporary storage. Chromium runs with its own in-process sandbox disabled, trading that layer for container isolation: the surrounding VM and the capability-drop layer take over the job instead. See [Isolation model](/docs/security/isolation/) for how that layering holds up across a project's containers.

The browser has network access and loads whatever public URL a call gives it. It also reaches your machine, but only over loopback: through `host.docker.internal`, anything listening on `127.0.0.1` is in reach. Keep that in mind if you run unauthenticated services there.