Skip to content

Latest commit

Β 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

chromary - Browser-as-a-Service API

A minimal Browserless-like service that allows users to programmatically create and manage headless browser instances on demand.

Features

  • Launch headless Chromium browsers via REST API
  • Get WebSocket URLs for Playwright/Puppeteer connections
  • List all browser instances with status and connection info
  • Automatic lifecycle management (idle timeout, expiration, cleanup)
  • Health monitoring and restart capabilities

Quick Start

Prerequisites

  • Python 3.10+
  • Docker (for PostgreSQL)
  • uv (Python package manager)
# Install uv if not already installed
curl -LsSf https://astral.sh/uv/install.sh | sh

1. Start the Database

docker compose up -d db

2. Install Dependencies

cd backend
uv sync

3. Install Playwright Browsers

uv run playwright install chromium

4. Run Database Migrations

uv run alembic upgrade head

5. Start the Backend Server

uv run fastapi dev app/main.py

The API will be available at http://localhost:8000

Testing the Service

Run the Demo Script

cd backend
uv run python scripts/test_browser_service.py

This script will:

  1. Create a browser instance via the API
  2. Connect to it using Playwright's CDP connection
  3. Run automation demos (navigate, screenshot, form fill)
  4. Save screenshots to /tmp/browser_service_screenshots/
  5. Terminate the browser

Expected Output

============================================================
πŸš€ Browser-as-a-Service Demo
============================================================
πŸ“¦ Creating browser instance...
βœ… Browser created: 089d52fe-a976-4199-aeb7-c723e95f820e
   Status: running
   CDP Port: 36123
   WebSocket URL: ws://127.0.0.1:36123

🌐 Connecting to browser via CDP...
βœ… Connected to browser via CDP!

πŸ”— Demo 1: Navigate to example.com
   Title: Example Domain
   πŸ“Έ Screenshot: /tmp/browser_service_screenshots/01_example_com.png
...
✨ Demo completed!

API Endpoints

Method Endpoint Description
POST /api/v1/browsers/ Create a new browser instance
GET /api/v1/browsers/ List all browser instances
GET /api/v1/browsers/{id} Get browser details
DELETE /api/v1/browsers/{id} Terminate a browser
POST /api/v1/browsers/{id}/keepalive Reset idle timeout
POST /api/v1/browsers/{id}/restart Restart stopped browser
WS /api/v1/browsers/{id}/ws WebSocket proxy for CDP

API Usage Examples

Create a Browser

curl -X POST http://localhost:8000/api/v1/browsers/ \
  -H "Content-Type: application/json" \
  -d '{
    "idle_timeout_seconds": 300,
    "max_lifetime_seconds": 3600
  }'

Response:

{
  "id": "089d52fe-a976-4199-aeb7-c723e95f820e",
  "status": "running",
  "websocket_url": "ws://127.0.0.1:36123",
  "cdp_port": 36123,
  "idle_timeout_seconds": 300,
  "max_lifetime_seconds": 3600
}

Connect with Playwright (Python)

from playwright.async_api import async_playwright

async def connect_to_browser(cdp_port: int):
    async with async_playwright() as p:
        browser = await p.chromium.connect_over_cdp(f"http://127.0.0.1:{cdp_port}")
        page = await browser.new_page()
        await page.goto("https://example.com")
        await page.screenshot(path="screenshot.png")
        await browser.close()

Connect with Playwright (Node.js)

const { chromium } = require('playwright');

async function connectToBrowser(cdpPort) {
  const browser = await chromium.connectOverCDP(`http://127.0.0.1:${cdpPort}`);
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({ path: 'screenshot.png' });
  await browser.close();
}

List All Browsers

curl http://localhost:8000/api/v1/browsers/

Terminate a Browser

curl -X DELETE http://localhost:8000/api/v1/browsers/{browser_id}

Running Tests

cd backend
uv run pytest tests/api/routes/test_browsers.py tests/api/routes/test_ws.py -v

Project Structure

backend/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ api/routes/
β”‚   β”‚   β”œβ”€β”€ browsers.py      # Browser CRUD endpoints
β”‚   β”‚   └── ws.py            # WebSocket proxy endpoint
β”‚   β”œβ”€β”€ services/
β”‚   β”‚   β”œβ”€β”€ browser_manager.py  # Playwright browser lifecycle
β”‚   β”‚   β”œβ”€β”€ ws_proxy.py         # CDP WebSocket proxy
β”‚   β”‚   └── scheduler.py        # Background task scheduler
β”‚   β”œβ”€β”€ tasks/
β”‚   β”‚   └── cleanup.py       # Idle/expiry cleanup tasks
β”‚   β”œβ”€β”€ models.py            # Database models
β”‚   └── main.py              # FastAPI app entry point
β”œβ”€β”€ scripts/
β”‚   └── test_browser_service.py  # Demo script
└── tests/
    └── api/routes/
        β”œβ”€β”€ test_browsers.py
        └── test_ws.py

Configuration

Environment variables (set in .env):

Variable Default Description
BROWSER_MAX_CONCURRENT 10 Max concurrent browser instances
BROWSER_DEFAULT_IDLE_TIMEOUT 300 Default idle timeout (seconds)
BROWSER_DEFAULT_MAX_LIFETIME 3600 Default max lifetime (seconds)

Browser States

Status Description
pending Created but not yet started
running Browser is active and accepting connections
sleeping Idle timeout reached, process stopped
stopped Gracefully terminated
error Failed to start or crashed

Tech Stack

  • FastAPI - Web framework
  • PostgreSQL - Database
  • SQLModel - ORM
  • Playwright - Browser automation
  • APScheduler - Background tasks
  • uv - Package manager

About

A managed control plane for real browsers. Spin up isolated headless browser instances, connect over WebSocket using Playwright or Puppeteer, and run long-lived sessions with built-in sleep, restart, and time-limit controls.

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Contributors

Languages