# Container sessions

> Run browsers for Browser Harness in a reusable Playwright or Camoufox Docker container.

Run the browser in a warm Docker container while your application keeps running the tools. Browser Harness starts or reuses one server container and gives each run its own browser connection, so concurrent runs share the container.

## Prerequisites

- The [TypeScript API](/harness/library/) installed.
- Docker, on a trusted host. Docker access is privileged: it can start containers, mount host paths and change local infrastructure.

## Run a Playwright container

1. Start a session, open a page and disconnect when done:

   ```ts
   import {
     startPlaywrightContainer,
     stopPlaywrightContainer,
   } from "@blopai/browser-harness";

   const session = await startPlaywrightContainer();
   try {
     const context = await session.browser.newContext();
     const page = await context.newPage();
     await page.goto("http://host.docker.internal:3000");
   } finally {
     await session.stop();
   }
   ```

   `session.stop()` disconnects this run and keeps the container warm. The server publishes a random port on `127.0.0.1`, and `host.docker.internal` reaches the host.

2. Remove the shared container when no run needs it:

   ```ts
   await stopPlaywrightContainer();
   ```

## Run a Camoufox container

Use `startCamoufoxContainer()` and `stopCamoufoxContainer()` the same way, only when you need the Camoufox fingerprint. Create contexts with `{ viewport: null }`. Without an image override, Browser Harness builds a local image matched to its versions. The server uses a random WebSocket path, and only Camoufox settings pass from the environment into the browser launch.

## Check it worked

While a container is warm, Docker lists it:

```bash
docker ps --filter name=blop-playwright
```

## Join a shared Docker network

When your app and the browser run in separate containers, put both on one trusted network:

```bash
export BLOP_PLAYWRIGHT_NETWORK=blop-network
export BLOP_CAMOUFOX_NETWORK=blop-network
```

The browser endpoint then uses the container name instead of a published port. Every container on that network can reach the browser server, so do not attach untrusted workloads.

## Container environment variables

| Variable | Default | Effect |
| --- | --- | --- |
| `BLOP_PLAYWRIGHT_IMAGE` | `mcr.microsoft.com/playwright` matching the Playwright version | Playwright image |
| `BLOP_PLAYWRIGHT_CONTAINER` | `blop-playwright` | Playwright container name |
| `BLOP_PLAYWRIGHT_NETWORK` | unset | Shared Docker network |
| `BLOP_CONTAINER_DISABLE_CORS_BYPASS` | unset | Any non-empty value keeps Chromium's same-origin enforcement on |
| `BLOP_CAMOUFOX_IMAGE` | locally built image | Camoufox image |
| `BLOP_CAMOUFOX_CONTAINER` | `blop-camoufox` | Camoufox container name |
| `BLOP_CAMOUFOX_NETWORK` | `BLOP_PLAYWRIGHT_NETWORK` | Camoufox Docker network |

The Playwright container disables Chromium web security by default so runs can cross origins, for example OAuth redirects and third-party iframes. Set `BLOP_CONTAINER_DISABLE_CORS_BYPASS` when same-origin behaviour is part of what you test.

## Next

- [Security model](/harness/security/)
