# Desktop recipe

URL: https://briq.cloud/cookbook/desktop

> A screen with Chromium on it, for an agent to use a website like a person: Playwright MCP for the page, an MCP server for the whole screen in pixels (screenshot, click, type), and a view at the private URL to watch and take over. The browser profile stays on its volume, which is never forked or backed up.

Tags: sandbox, browser

## Run it

briq.yaml:

```yaml
stack: app
services:
  desktop:
    recipe: desktop
    # its defaults, each a field you can set:
    # image: ghcr.io/briq-cloud/sandbox:desktop
    # size: small
    # volume: { gb: 2 }
```

MCP:

```ts
briq_run({ recipe: "desktop", wait: true })
```

REST:

```bash
curl -X POST https://api.briq.cloud/v1/briqs \
  -H "Authorization: Bearer $BRIQ_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "recipe": "desktop",
    "wait": true
  }'
# → { "briqs": [{ "id": "bq_…", … }], "outputs": { "DESKTOP_MCP_URL": "…" }, … }

# ... when done, for each briq id
curl -X DELETE https://api.briq.cloud/v1/briqs/<id> \
  -H "Authorization: Bearer $BRIQ_API_KEY"
```

## Fields

Set them beside `recipe`, in briq_run or a briq.yaml service, as a GitHub Actions service.

| Name | Type | Options | Default | What it does |
| --- | --- | --- | --- | --- |
| `image` | string | — | "ghcr.io/briq-cloud/sandbox:desktop" | A date tag of the same image (ghcr.io/briq-cloud/sandbox:desktop-2026-10-27) to keep the versions |
| `env` | record | { key: string \| { secret, value?, scope?, hosts?, locations? } } | {} | Added to the desktop's environment. DESKTOP_SIZE sets the screen (1280x800 by default, what the computer-use tools expect) |
| `size` | enum | small, medium, large | "small" | small for a few tabs; medium for heavy pages or several at once |
| `volume` | object | { gb? } | {"gb":2} | The volume at /work, the browser profile and downloads: { gb } |

## Outputs

Returned by `briq_run` once the stack is healthy. Hosts resolve inside your team network; secrets are generated per run.

| Output | Value |
| --- | --- |
| `DESKTOP_MCP_URL` | `http://desktop.<stack>.internal:8931/mcp` |
| `BROWSER_MCP_URL` | `http://desktop.<stack>.internal:8931/playwright/mcp` |
| `DESKTOP_TOKEN` | `<DESKTOP_TOKEN>` |

## What it runs

| Briq | Image | Ports | Size | Volume |
| --- | --- | --- | --- | --- |
| desktop | `ghcr.io/briq-cloud/sandbox:desktop` | 8080, 8931 | small | 2 GB at /work |

## When to use it

* An agent must use a website like a person: sign in, click through, fill a form, read what is on
  the screen.
* You want to watch what it does, or take over for a sign-in or a second factor.
* A browser session that keeps its tabs and sign-ins between tasks.

```text
briq_run({ recipe: "desktop", stack: "home-ana", expose: "private" })
```

## Connect an agent

The run's outputs give two MCP servers, on the team's private network:

| Output            | What it drives                                                                               |
| ----------------- | -------------------------------------------------------------------------------------------- |
| `BROWSER_MCP_URL` | The page, by its accessibility tree: Playwright MCP (navigate, click an element, fill, read) |
| `DESKTOP_MCP_URL` | The whole screen in pixels: `screenshot`, `zoom`, `click`, `type`, `key`, `scroll`, `drag`   |

Both take `Authorization: Bearer <DESKTOP_TOKEN>`. A briq of the same stack has the token in its
env when run after the desktop with `env: { DESKTOP_TOKEN: "{{secret:DESKTOP_TOKEN}}" }`, or for
one command with `briq_exec`'s `env`. Then, for Claude Code there:

```json
{ "mcpServers": {
  "browser": { "type": "http", "url": "<BROWSER_MCP_URL>", "headers": { "Authorization": "Bearer ${DESKTOP_TOKEN}" } },
  "desktop": { "type": "http", "url": "<DESKTOP_MCP_URL>", "headers": { "Authorization": "Bearer ${DESKTOP_TOKEN}" } }
} }
```

## Tips

* **The page first, pixels when the page is not enough**: the accessibility tree is faster and
  cheaper than screenshots; pixels reach dialogs, file pickers and canvas apps.
* **Watch it**: open the URL; the view connects by itself, view only. To take the mouse and
  keyboard, turn off View only in its side panel.
* **Signed in once**: the browser profile is on the volume, so sites stay signed in through sleep
  and restarts. The volume is never copied: a desktop cannot be forked.
* **Screen size**: `env: { DESKTOP_SIZE: "1280x800" }`, the default, fits the computer-use tools
  without scaling.
* **Private URL only**: the view takes the mouse of a signed-in browser, so `expose: "public"` and
  a TCP endpoint are refused.
* **No CAPTCHA solving, no stealth**: when a site asks for a person, take over in the view.
* **`small` for a few tabs**; `medium` for heavy pages.
* **Every version** (Chromium, Playwright MCP, noVNC): the image's
  [REFERENCE-desktop.md](https://github.com/briq-cloud/sandbox/blob/main/REFERENCE-desktop.md).
