This release adds 2 notable features for engineering teams evaluating rollout.
✓ No known CVEs patched in this version
Topics
+4 more
Summary
AI summaryFirst 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
About Veil
All releases →Related context
Beta — feedback welcome: [email protected]