Skip to content

Veil

vpython-v1.3.1 scope: python Feature

This release adds 2 notable features for engineering teams evaluating rollout.

✓ No known CVEs patched
Read the diff → Tool health → What is this tool? →

✓ No known CVEs patched in this version

Topics

ai-agents anti-detection automation browser-automation cdp chrome-devtools-protocol
+4 more
mcp stealth-browser typescript web-scraping

Summary

AI summary

First release of the Python front end for Veil browser automation

Full changelog

First release of the Python front end. Same browser, same stealth, Python API.

pip install veilbrowser-1.3.1-py3-none-any.whl        # attached below
# or straight from the repo:
pip install "git+https://github.com/acunningham-ship-it/[email protected]#subdirectory=python"
import asyncio
from veilbrowser import Browser, Fingerprint

async def main():
    async with await Browser.launch(fingerprint=Fingerprint.preset("windows-chrome")) as b:
        page = await b.new_page()
        await page.goto("https://example.com")
        print(await page.title())
        await page.screenshot("shot.png")

asyncio.run(main())

Drive a profile you're already signed into

Chrome locks a user-data-dir, so the profile carrying your real sessions cannot be opened by a second instance. Attach instead of launching:

b = await Browser.connect("127.0.0.1:9222")   # ws:// URL, http:// origin, or host:port
page = (await b.pages())[0]                    # the tab that's already open
await b.close()                                # detaches — never kills a browser it didn't start

That's the difference between "works on sites that allow anonymous access" and "works on the sites that matter": Google, Reddit, Meta and TikTok score the session, not the IP.

The stealth layer has one implementation, not two

The injected script, launch flags, profile identities and keystroke table are generated from the TypeScript source into veilbrowser/_assets/, and tests/python-parity.test.ts fails the build if Python's assembled script differs from the TypeScript one by a single byte.

That's deliberate. Two hand-maintained copies of a stealth patch drift, and a drifted patch doesn't fail loudly — one front end just becomes detectable while all of its own tests stay green.

Verified (21/21, from the installed wheel, outside the source tree)

navigator.webdriver false · UA / platform / screen / timezone / WebGL from the profile · patched getters report [native code] and are inherited from the prototype rather than own-properties · canvas noise stable across reads (a per-call random would itself be the tell) · real keyCodes, not 0 · fragment goto 6 ms vs 110 ms full load · close() on an attached browser leaves it alive.

API

Browser.launch/connect/new_page/pages/close · page.goto/evaluate/click/fill/type/press/select/wait_for_selector/wait_for/wait_for_response/text/attribute/title/content/inner_text/url/screenshot/pdf/get_cookies/set_cookies/scroll/set_viewport/block_private_network · Fingerprint.preset/random

One dependency (websockets); everything else is stdlib.

evaluate() takes a string, not a callable — Python can't serialise a function to JS. An arrow function passed as a string evaluates to a function object, which can't be returned by value, so wrap it: "(() => document.title)()".

Deliberately not ported

The snapshot/ref system (selectors are what Python callers write), auto-Xvfb (set DISPLAY or use xvfb-run; it degrades to headless rather than failing), downloads, FedCM, frame switching. Fingerprint.random(seed) is deterministic but does not produce the same identity as the TypeScript random(seed) — different PRNGs, and nothing needs the two languages to agree on a random identity, only on the stealth script.

Full API: python/README.md

Weekly OSS security release digest.

The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.

No spam, unsubscribe anytime.

Share this release

Track Veil

Get notified when new releases ship.

Sign up free

Beta — feedback welcome: [email protected]