dots: The AI browser agent that makes the browser the whole product
A thin Python CLI wraps a stealth-patched Firefox engine, deterministic fingerprints, and MCP integration into a local-first way to browse the live web without looking like a bot.
The fingerprint is decided inside the engine, not painted over with JavaScript that a page can inspect. One identity per seed. Screen, fonts, GPU, timezone and language agree with each other, and --seed gives back the same person on every run.
- dots is interesting because it treats browser survivability as the core product, not a side effect of better prompting or orchestration.
- The repo is a thin facade over a much heavier Firefox-based stealth engine, which is the opposite of most browser-agent projects.
- Its `--seed` model turns fingerprinting into a reproducible identity system, so the browser looks coherent instead of randomly spoofed.
- The project's discipline shows up in the wrapper, the CI gates, and the narrow scope, which together make the package feel much bigger than its codebase.
The smallest repo with the biggest browser. That is the trick here. `dots` looks like a thin Python package, but it fronts a much heavier machine: a patched Firefox stack, a deterministic identity model, and an MCP-friendly interface built for hostile web surfaces.
That matters because most browser agents fail for a boring reason. They can reason, but they still show up as a bot. A site does not need to defeat the model if it can reject the browser first.
Why the browser is the real bottleneck
The usual automation stack starts with Playwright, Selenium, or a browser-agent framework, then adds stealth on top. `dots` starts lower. It assumes the browser is the battlefield, and that anti-bot systems are watching the body, not the prompt.
| Layer | Standard browser automation | dots |
|---|---|---|
| Primary goal | Run pages and scripts | Survive hostile detection surfaces |
| Stealth strategy | JavaScript shims and patches | Engine-level browser identity |
| Identity model | Often ad hoc or random | Deterministic and seed-based |
| User interface | Framework-first | CLI-first, branded wrapper |
| Deployment style | Generic automation stack | Local-first browser persona |
dots turns identity into a reproducible system
The most distinctive idea in the repo is not stealth by itself. It is coherence. `--seed` is presented as a way to generate one believable browser person, not a grab bag of spoofed values that happen to dodge one test today.
That distinction is subtle, but it is the whole story. A browser that randomizes everything can look suspicious because it contradicts itself. A browser that keeps GPU, fonts, timezone, screen size, and event timing in agreement looks less like a patchwork and more like a person.
The CLI is the product, the engine is the moat
This is where the package earns its shape. The user lands on a clean command line, while the real complexity lives in the dependency chain and the browser engine underneath. The facade is not cosmetic. It is the product boundary.
# src/dots/cli.py
from invisible_playwright_mcp.cli import main as mcp_main
# dots adds a branded entry point and passes the hard work through.
# The command surface stays small even as the browser layer stays complex.
def main():
mcp_main()
That move is clever because it separates trust from implementation. Users do not need to understand the patched browser internals to benefit from them. They need a stable command, a repeatable identity, and a browser that behaves like it belongs on the site.
A disciplined ecosystem, not just a script
The repo's credibility comes from the boring parts: CI, hooks, tests, and claim-checking. That discipline matters in a stealth product, because promises about browser behavior are easy to oversell and hard to verify.
| Discipline | What it signals | Why it matters |
|---|---|---|
| Strict tests | The wrapper does what it says | Prevents silent breakage in a fragile stack |
| Pre-push hooks | Quality gates before publish | Keeps the repo honest before code ships |
| Pinned dependencies | Controlled runtime surface | Reduces surprise in a browser-sensitive tool |
| Lean codebase | A narrow product scope | Makes the hidden engine easier to trust |
The result is a repository that feels bigger than its line count. It is not bloated, but it is not casual either. It is a carefully narrowed interface around a system that has to stay coherent to work.
How it compares to the rest of the agent stack
`dots` is not trying to be the broadest browser agent. It is trying to be the one that survives on the most hostile surfaces. That puts it in a different class from general-purpose automation tools and cloud-first computer-use products.
| Project type | Strength | Trade-off |
|---|---|---|
| Playwright or Selenium style agents | General automation and familiar APIs | Often easier to detect |
| Browser-use or Skyvern | Higher-level task orchestration | Still depends on the browser layer underneath |
| Camoufox or invisible-browser tooling | Stealth at the browser layer | Less productized as a user-facing agent |
| Cloud computer-use products | Managed experience and orchestration | Less local control and less identity coherence |
| dots | Local-first stealth wrapper with seedable identity | Narrower scope, but stronger fit for hostile live sites |
That is the point. `dots` does not win by being a universal agent platform. It wins by focusing on the narrow layer where most agents actually fail. The browser is the body, and this repo is built for the body, not the brain.
The bigger implication: agents need bodies
The broader lesson is simple. Agent tooling is moving past pure reasoning problems. If a model cannot inhabit a browser that looks real enough to pass scrutiny, the rest of the stack barely matters.
`dots` is a reminder that the next wave of agent infrastructure may be less about clever prompts and more about credible embodiment. The browser itself becomes the product, because the browser is where the work gets admitted or denied.