Damru Python API Reference

Damru exposes four primary Python entry points — AsyncDamru, Damru, DamruPool, and DamruPoolSync — each of which returns a standard Playwright BrowserContext with all 8 stealth layers already applied, so existing Playwright code requires no changes beyond the context manager.

This page covers every class, parameter, method, and helper exported by the damru package. For installation and environment setup, see Installation. For CLI commands, see CLI Reference.


Requirements

RequirementDetail
HostUbuntu 24.04 LTS (native) or Ubuntu WSL2 with Damru’s bundled kernel
Android backendRooted Redroid in Docker (MuMu Player is experimental and not recommended)
Python3.10 or higher
Playwright>=1.40, <1.60
Other dependenciesrequests, pysocks
Optionalcurl_cffi (highly recommended for TLS/JA3 impersonation via damru.bypass)

Core Classes

AsyncDamru is the primary asynchronous context manager. It orchestrates the 8 stealth layers — Android props, GPU binary patching, syscall interception, CDP overrides, worker interception, Chrome flag patching, OS-level evasions, and display spoofing — then returns a Playwright BrowserContext.

Import and Basic Usage

from damru import AsyncDamru

async with AsyncDamru(device="pixel_8_pro", proxy="socks5://...") as context:
    page = await context.new_page()
    await page.goto("https://creepjs.com")

__init__ Parameters

ParameterTypeDefaultDescription
devicestr"random"Device profile slug, marketing name, or model (e.g., "samsung_galaxy_s24_ultra", "Pixel 8 Pro", "pixel_8_pro"). "random" picks from the premium pool.
profile_tierstr"premium" (from config)Random pool when device is unset or "random". Options: premium, premium_verified, premium_new, medium, experimental, extended, all. Explicit named devices ignore this filter.
serialstrNoneADB serial of the target worker. If None, auto-detects virtual devices: TCP Redroid endpoints first, then emulator-*. Physical USB serials are refused by default.
proxystrNoneSOCKS5 or HTTP proxy URL for GeoIP resolution and Python-side proxy checks. Used to derive Android timezone and locale when http_proxy is not set.
http_proxystrNoneAndroid system HTTP proxy as host:port or http://user:pass@host:port. When present, Damru resolves GeoIP through this path because it is the route Chrome actually uses.
timezonestrNoneForce a specific IANA timezone (e.g., "America/New_York"). Leave unset to auto-derive from proxy exit.
localestrNoneForce a specific BCP-47 locale (e.g., "en-US", "pt-BR"). Leave unset to auto-derive from proxy country.
webrtc_blockboolTrueDrops all outgoing UDP WebRTC traffic at the kernel level via iptables. Set to False to enable proxy IP spoofing for WebRTC instead of blocking.
debugboolFalseEnables verbose console logging for OS patches and CDP overrides.

Proxy and timezone notes:

Methods and Properties

NameTypeDescription
await new_page()methodCreates a new stealth-hardened Playwright Page. Damru normalizes the new Chrome tab before returning, so you can navigate immediately.
browserpropertyAccess the underlying Playwright Browser object.
pagespropertyList of currently open Playwright Page objects.
profilepropertyReturns the active DamruProfile (device specs, User-Agent, client hints, etc.).

Damru — Synchronous

Damru is the blocking wrapper around AsyncDamru, suitable for traditional scripts and multi-threaded environments. Methods and parameters are identical to AsyncDamru but synchronous — no await.

Usage

from damru import Damru

with Damru(device="pixel_8_pro") as context:
    page = context.new_page()
    page.goto("https://bot.sannysoft.com/")
    page.wait_for_timeout(5000)
    page.screenshot(path="sannysoft.png")
    print("Passed Sannysoft!")

Exception Handling

DamruError

All Damru-specific failures — ADB connection errors, rooting failures, binary patching errors — raise DamruError.

from damru import Damru, DamruError

try:
    with Damru() as browser:
        page = browser.new_page()
        page.goto("https://example.com")
except DamruError as e:
    print(f"Damru failed to initialize: {e}")

Pool Management

DamruPool — Async

DamruPool orchestrates parallel automation across multiple Redroid Docker containers, managing container lifecycles, port forwarding, and proxy rotation automatically.

Usage

from damru import DamruPool

async with DamruPool(mode="auto", max_devices=2) as pool:
    async with pool.session() as context:
        page = await context.new_page()
        await page.goto("https://example.com")
        print(await page.title())

__init__ Parameters

ParameterTypeDefaultDescription
max_devicesintconfig NUM_DEVICES (default 10)Number of concurrent Redroid containers to maintain. Use 0 in "manual" mode to use every detected ADB device.
modestrconfig MODEDeployment mode: "auto" manages Redroid via Docker (production path), "manual" uses existing ADB devices, "mumu" is experimental.
proxystrconfig PROXYSingle proxy shared by all pool sessions.
proxieslist[str]config PROXIESPer-worker proxy list. Pool rotates through these by slot index.
http_proxystrconfig HTTP_PROXYAndroid system HTTP proxy when it differs from the browser proxy. GeoIP is resolved through this path when set.
http_proxieslist[str]config HTTP_PROXIESPer-worker Android HTTP proxy list.
devicestrconfig DEVICEFixed device profile for all sessions, or None/"random" for per-session random profiles.
profile_tierstr"premium" (from config)Random pool for sessions without a fixed device.
timezonestrconfig TIMEZONEForce timezone instead of deriving from proxy.
localestrconfig LOCALEForce locale instead of deriving from proxy country.
chrome_apkstrconfig CHROME_APKChrome APK directory for raw/unbaked Redroid. Leave unset to auto-discover from the APK bundle and allow Chrome rotation.
wsl_distrostrconfig WSL_DISTROWindows only: WSL distro that owns Docker/Redroid.

pool.session() — Context Manager

Provides an AsyncDamru context (a Playwright BrowserContext with all stealth layers applied) from an available container in the pool.

async with pool.session() as context:
    page = await context.new_page()
    await page.goto("https://example.com")

pool.open_url() — One-Shot Navigation

Convenience wrapper for single stealth navigations without managing the session context manually:

title = await pool.open_url("127.0.0.1:5600", "https://example.com", proxy="socks5://user:pass@host:1080")
print(f"Title: {title}")

DamruPoolSync — Synchronous

The synchronous counterpart to DamruPool, suitable for multi-threading frameworks such as concurrent.futures.

Usage

from damru import DamruPoolSync

proxies = [
    "socks5://proxy1:1080",
    "socks5://proxy2:1080",
    "socks5://proxy3:1080",
]

with DamruPoolSync(mode="auto", max_devices=3, proxies=proxies) as pool:
    with pool.session() as context:
        page = context.new_page()
        page.goto("https://example.com")
        print(page.title())

Parameters are identical to DamruPool.


Device Management

Profile Database

Damru ships 155 real Android device profiles in damru/devices.py. For the full generated list including GPU families, chipsets, screen variants, and Android SDK versions, see the Device Profiles reference (upstream: docs/DEVICE_PROFILES.md).

Profile pool sizes:

PoolProfilesHow to activate
Premium (default)100device="random" or profile_tier="premium"
Medium38profile_tier="medium"
Experimental17profile_tier="experimental"
All155profile_tier="all"

Explicit named devices (device="Nokia C32", device="nokia_c32") are never filtered by the tier setting.

list_device_names()

Return all device profile names in the database.

from damru import list_device_names

names = list_device_names()
print(names[:5])  # e.g., ['Samsung Galaxy S24 Ultra', 'Samsung Galaxy S24', ...]

get_device(name_or_slug)

Return the AndroidDevice specification for a single profile by marketing name, model, or slug.

from damru import get_device

pixel = get_device("pixel_8_pro")
print(f"Cores: {pixel.hardware_concurrency}, RAM: {pixel.device_memory}GB")
print(f"GPU: {pixel.webgl_renderer}")
print(f"Screen: {pixel.screen_width}x{pixel.screen_height} @{pixel.dpi}dpi")

get_random_device()

Return a randomly selected AndroidDevice profile, optionally filtered by Android version or profile tier.

from damru import get_random_device

# Random premium profile
phone = get_random_device()

# Random Android 13 profile from any tier
phone_13 = get_random_device(android_version="13")

# Random medium-tier profile
medium = get_random_device(profile_tier="medium")

get_devices_by_tier(tier)

Return all profiles in a given tier.

from damru import get_devices_by_tier

all_profiles = get_devices_by_tier("all")
medium_profiles = get_devices_by_tier("medium")

Valid tier names: "premium", "premium_verified", "premium_new", "medium", "experimental", "extended", "all".


Async Profile Helper

force_device_profile(serial, device, **kwargs)

Apply a device profile to an already-running rooted worker without opening a full Playwright session. Equivalent to python -m damru force-profile from the CLI.

from damru import force_device_profile

result = await force_device_profile(
    "127.0.0.1:5600",
    "pixel_8_pro",
    timezone="America/New_York",
    locale="en-US",
)
print(result.description)

Parameters:

ParameterTypeDescription
serialstrADB serial of the target worker
devicestrProfile slug, name, or model
timezonestrIANA timezone override
localestrBCP-47 locale override
configure_chromeboolSet to False to skip Chrome command-line/preferences (--no-chrome)
configure_gpuboolSet to False to skip native Vulkan GPU layer
configure_memoryboolSet to False to skip native memory preload
clear_proxyboolClear the worker’s current Android HTTP proxy
browser_packagestrTarget package. Use "org.chromium.webview_shell" for WebView Shell harnesses

force_device_profile applies Android props, release string, timezone, locale, display size/density, CPU core spoofing, native Vulkan GPU spoofing, memory preload, and Chromium command-line/preferences by default. CDP runtime overrides (active-page specific) remain part of the runtime harness.


Advanced Configuration

Tune Damru’s global behavior via damru.config before initialization:

import damru.config

# WSL2 settings (Windows only)
damru.config.WSL_DISTRO = "Ubuntu-22.04"
damru.config.WSL_USERNAME = "my-wsl-user"
damru.config.WSL_PASSWORD = ""  # compatibility only

# Chrome APK path for raw/unbaked Redroid
damru.config.CHROME_APK = "/home/damru/chrome-apks/148.0.7778.178"

For first-run setup, prefer the CLI commands:

python -m damru setup
python -m damru install-image
python -m damru check-env
python -m damru check preflight

Advanced Modules

damru.bypass — Edge-Layer TLS Impersonation

Defeat CDN TLS fingerprinting and WAFs that inspect JA3/TLS handshake characteristics by replaying document navigations through curl_cffi browser impersonation.

from damru import AsyncDamru
from damru.bypass import arm_bypass_async

async with AsyncDamru() as browser:
    page = await browser.new_page()

    # Persistent TLS interception for all document navigations on this domain
    await arm_bypass_async(page, domain="target-site.com")

    # Document navigations now use randomized browser TLS hashes
    await page.goto("https://target-site.com")

curl_cffi is a separate install (pip install curl_cffi) and is not required for normal Damru sessions that rely on the 8 native stealth layers.


Cookbook — Common Patterns

Multi-Page Scraping in One Session

All pages opened within a single AsyncDamru context share the same spoofed device identity and IP:

import asyncio
from damru import AsyncDamru

async def main():
    async with AsyncDamru(device="random") as browser:
        page1 = await browser.new_page()
        page2 = await browser.new_page()

        await asyncio.gather(
            page1.goto("https://google.com"),
            page2.goto("https://bing.com"),
        )

asyncio.run(main())

Authenticated Proxy with Android HTTP Bridge

Android system proxy supports HTTP CONNECT. If your provider gives SOCKS5 for Python-side checks but Android Chrome must use a local HTTP bridge, pass both:

from damru import AsyncDamru

async with AsyncDamru(
    device="pixel_8_pro",
    proxy="socks5://user:pass@proxy.example:824",
    http_proxy="172.17.0.1:18888",
) as browser:
    page = await browser.new_page()
    await page.goto("https://demo.fingerprint.com/playground")

Damru resolves timezone and locale through http_proxy because that is the path Chrome actually uses. Do not set timezone or locale manually unless they match the current proxy exit.

Multi-Container Pool with Proxy Rotation

import asyncio
from damru import DamruPool

async def main():
    proxy = "socks5://user:pass@residential-proxy.com:1080"

    async with DamruPool(max_devices=2) as pool:
        async with pool.session() as ctx:
            page = await ctx.new_page()
            await page.goto("https://example.com")
            print(await page.title())

        # One-shot convenience wrapper
        title = await pool.open_url("127.0.0.1:5600", "https://shopee.com.br/", proxy=proxy)
        print(f"Title: {title}")

asyncio.run(main())

Synchronous Pool with Per-Worker Proxies

from damru import DamruPoolSync

proxies = [
    "socks5://proxy1:1080",
    "socks5://proxy2:1080",
    "socks5://proxy3:1080",
]

with DamruPoolSync(mode="auto", max_devices=3, proxies=proxies) as pool:
    for i in range(3):
        with pool.session() as context:
            page = context.new_page()
            page.goto("https://example.com/scrape-target")
            print(f"Worker {i}: {page.title()}")

Enable Debug Logging

async with AsyncDamru(debug=True) as browser:
    page = await browser.new_page()
    await page.goto("https://example.com")

Debug mode logs exact root commands (resetprop, iptables, wm size/wm density), binary patch steps, and CDP override calls.

Error Handling

from damru import AsyncDamru, DamruError

async def main():
    try:
        async with AsyncDamru(device="pixel_8_pro") as browser:
            page = await browser.new_page()
            await page.goto("https://example.com")
    except DamruError as e:
        print(f"Damru error: {e}")

asyncio.run(main())

WebView Shell Support

For WebView harness validation, apply a profile with --browser-package org.chromium.webview_shell via the CLI or pass browser_package="org.chromium.webview_shell" to force_device_profile. Damru writes /data/local/tmp/webview-command-line and patches app_webview/pref_store instead of Chrome’s paths. Chrome CDP remains the primary automation API; WebView Shell support is for WebView-specific debugging.

python -m damru force-profile --serial 127.0.0.1:5600 --device pixel_8_pro \
  --browser-package org.chromium.webview_shell

adb -s 127.0.0.1:5600 shell am start \
  -n org.chromium.webview_shell/.WebViewBrowserActivity \
  -a android.intent.action.VIEW -d https://example.com

For a custom Android app that embeds the system WebView, apply Android-level profile hardening with --no-chrome and launch the app separately:

python -m damru force-profile --serial 127.0.0.1:5600 --device pixel_8_pro \
  --no-chrome --proxy socks5://user:pass@host:port

adb -s 127.0.0.1:5600 shell am start \
  -n com.example.webview/.MainActivity \
  -a android.intent.action.VIEW -d https://example.com

Experimental Flags

Set these environment variables before starting a session to enable optional experimental layers:

FlagDescription
DAMRU_EXPERIMENTAL_SENSOR_HAL=1Native Android sensors (gyro/accel/mag) at 50–100 Hz via AIDL HAL. Requires baking the sensor HAL into the image once. Best stealth.
DAMRU_EXPERIMENTAL_HIDL_SENSOR_HAL=1Same but via older HIDL interface. Fallback if AIDL is broken on your image.
DAMRU_ENABLE_NATIVE_SENSOR_HAL=1Activates the sensor HAL at container start. Required when SENSOR_HAL=1.
DAMRU_EXPERIMENTAL_CDP_SENSORS=1Browser-level sensor faking via CDP. No image rebuild needed. Generic readings only.
DAMRU_EXPERIMENTAL_BATTERY_DUMPSYS=1Battery spoof via Android dumpsys.
DAMRU_EXPERIMENTAL_BATTERY_SPOOF=1Battery spoof via settings put global.
DAMRU_EXPERIMENTAL_WORKER_CORE_CDP=1Enable CDP worker-target auto-attach for multi-threaded JS. May destabilize Chrome.
DAMRU_EXPERIMENTAL_RAW_WORKER_CDP=1Skip reattach flow; keep raw CDP attached. Auto-set by --mode cdp.
DAMRU_PROFILE_TIER=allEquivalent to profile_tier="all" — includes medium and experimental device profiles in random selection.
# Linux/WSL
export DAMRU_EXPERIMENTAL_SENSOR_HAL=1
export DAMRU_ENABLE_NATIVE_SENSOR_HAL=1
python your_script.py