OpenAI used its DevDay to put the Dots name on a hosted, always-on agent. Almost the same day, a repository called feder-cr/dots appeared on GitHub, created at 23:06 UTC on September 29, 2026. By the October 2, 2026 snapshot it had 2,416 stars and 421 forks, it is MIT licensed, and the README ends with a line worth quoting in full: Not affiliated with OpenAI. MIT licensed.
This SOP does one job: get the open-source dots running on your own machine, and get it running well. Every command below is copied verbatim from the repository README; no parameter has been rewritten. Every number comes from the October 2 snapshot or from the README itself. If you already maintain a local-first self-hosted agent such as OpenClaw, this site's OpenClaw 2.0 upgrade and migration SOP [/en/posts/openclaw-2-0-upgrade-migration-sop] covers that route; dots is a different shape of self-hosting.
What this agent is
Three sentences. One: every AI agent is a model and a browser; you can swap the model with one flag, and the browser is what the website sees. That is the README's own framing. Two: open-source dots ships with a real Firefox-engine browser, and pointed at any model on OpenRouter it becomes an agent that operates web pages on your behalf. Three: it runs on your machine, not in anyone's cloud, and its interface is a local web page on a local port.
Why the engine matters at a technical level is a story for a separate article. This one is about getting it running.
What you need before you start
Three things, plus one rule.
First, an OpenRouter API key. dots will not start without --openrouter-key, and all model calls go through OpenRouter, so the key comes from there: create an account, open the Keys page, create a new key. Keys use the sk-or- prefix, and the sk-or-... in the README command is a placeholder for exactly that. Save the key into your password manager the moment you create it; do not paste it into chat windows.
Second, a Windows or Linux machine. The README documents exactly these two routes, and macOS users should wait for official documentation rather than improvising. You do not need to pre-install Python, and you do not need to install uv by hand. The install script sets up uv, and uvx handles the runtime environment, which is why the whole setup fits in three lines.
Third, network reach to three places: astral.sh, where the uv install script lives; GitHub, where the dots source lives; and OpenRouter, where the model calls go. The first run pulls the entire repository and its dependencies, so if any of those three is unreachable, everything stalls.
The rule: this key is the key to your wallet. It does not go into git, into screenshots, or into shared documents.
Three commands and you are running
Both routes are three lines, copied verbatim from the README.
Windows, in PowerShell:
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
$env:Path = "$env:USERPROFILE\.local\bin;$env:Path"
uvx --from git+https://github.com/feder-cr/dots dots --openrouter-key sk-or-...Linux:
curl -LsSf https://astral.sh/uv/install.sh | sh
source $HOME/.local/bin/env
uvx --from git+https://github.com/feder-cr/dots dots --openrouter-key sk-or-...Line by line, what each one does and why it is there.
Line one installs uv, Astral's Python tooling manager. This is how dots solves the "where does my environment come from" problem: you never pick a Python version, you never create a virtualenv, you never resolve dependencies by hand. The ByPass flag in the PowerShell line applies only to the child process that one launch creates; it does not change your system's execution policy.
Line two puts uv's install location on the current session's PATH. On Windows it appends the .local\bin folder under your user profile to the Path variable; on Linux it sources the official env script. The change affects the current window only. If a terminal you open later cannot find the uv command, that session's PATH is the first thing to check.
Line three is the real launch. uvx pulls the feder-cr/dots repository from GitHub, installs its dependencies inside an isolated environment, and runs the dots command within it, passing your key through --openrouter-key. The sk-or-... is a placeholder; replace it with your full key. The first run is noticeably slower than every later run, because it is downloading code and installing dependencies. That is normal. Do not kill it at minute two.
When the process is up, open http://127.0.0.1:8765 in your browser, and you are in business.
The 8765 page: conversation left, browser right
The README describes this screen in a single line: the conversation on the left, the browser on the right, live.
That line deserves its own section, because it is the biggest experiential difference between self-hosting and a hosted product. You type a task on the left; the Firefox instance on the right shows you every action in real time. Which URL it opens, where it moves the pointer, what it fills in, what the page returns, all of it visible while it happens. When something goes wrong you do not dig through logs to reconstruct what happened; you watch where it stopped. When a task finishes, the run itself is your delivery evidence, judged with your own eyes.
One nuance: the interface runs locally on your machine, but model calls still leave it through OpenRouter.
Swapping the model: one parameter
The README states it plainly: the model is any model on OpenRouter, and --model changes it.
This is the most practical benefit of the self-hosted route. Same browser, same task prompt, same interface, but the model layer is entirely your decision. A cheaper model for routine lookups, a stronger model for tasks with more steps and more ways to go wrong. To switch, append --model and a model name to the launch command; nothing else moves. The browser behavior and the model are decoupled, which is the engineering consequence of the README's opening promise: you can swap the model with one flag.
It also means your cost model is transparent: you pay OpenRouter for tokens, per model, and can weigh that against what each task is worth.
Four parameters worth learning
The README does not enumerate many parameters, but each one maps to a real need. Four, in rough order of usefulness.
--seed: one identity per seed. Screen, fonts, GPU, timezone and language agree with each other, and the same seed produces the same person on every run. If you have a recurring task where you want a consistent environment from day to day, fix the seed. If you are debugging and want to reproduce what happened in a past run, reuse that run's seed and the environment lines up again.
--profile-dir: a browser that remembers. Logins and cookies persist across runs when the profile is pinned to a directory. Tasks that need a signed-in state, such as checking your own bookings or managing your own dashboard, should not walk through the login flow every single time. Pin the profile once, and from the second run onward the session is already there. For a broader view of how agent credentials and permissions should be handled, and where current approaches differ, this site's comparison of agent credential and permission models [/en/posts/agent-credential-permission-comparison-review] is the companion read.
--proxy: where it connects from is who it is. With a proxy attached, the timezone and the language follow the exit point. Combined with --seed, this produces internally consistent identities; the README's stated design intent is that the features agree with each other, not that any single field is altered in isolation. And because this parameter exists, you should have a clear answer to where you point it and why. The compliance section below returns to this point.
--help: the index to everything. The three parameters above are only the ones the README chooses to highlight. The complete list is one command away:
dots --helpPlugging it into your own CLI
dots is a complete product with an interface, but the browser underneath can be detached and reused. The same organization publishes a companion repository, invisible_playwright_mcp, and the README is explicit about what it does: Claude Code, Codex, Gemini CLI, or any MCP client can attach this browser as a server; dots is its interface.
What that means in daily work: when your coding agent needs to read a page, fill a form, or check something behind a login, it no longer waits for you to do that part by hand. It uses the same browser backend directly. If your Codex setup does not yet talk to external tools, read this site's Codex harness integration SOP [/en/posts/codex-harness-integration-sop] first, then come back and attach this server. If you want the fuller picture of how computer-use agents are built from scratch, the build-your-own SOP [/en/posts/computer-use-agent-build-sop] is the longer companion piece.
The sample task, and the discipline inside it
The README ships exactly one example task. In substance: go to a given URL, search one-way economy flights for one adult from Milan to Lisbon, read the cheapest fare for every date from the 12th to the 16th of next month, and, the part that matters most, if a date has no availability, say so; do not guess a number.
The phrasing is worth studying, because it is a template disguised as a demo. It supplies four things in a single prompt: an explicit URL, explicit filters, an explicit date range, and the sentence that closes the hallucination exit. "If a date has no availability, say so. Do not guess a number" reads as plain good manners, but it decides whether you can trust the delivered result. When guessing is explicitly forbidden, every number that comes back is one you can use; when it is not, you are re-checking everything anyway, and the agent has saved you nothing.
Whatever you hand to this agent, or to any agent, is worth writing in the same shape: conditions, range, and what to do when uncertain. All three, every time. A vague brief does not produce a cautious agent; it produces a confident one, which is worse.
Compliance boundaries
This section is not a legal disclaimer bolted on at the end. It is part of the setup, and it should be read as a precondition for everything above.
First, respect the target site's terms of service and local law. Sites differ widely in their stance toward automated access, and whether your use case is permitted is a judgment only you can make.
Second, automate only accounts you are authorized to access and your own business. Checking your own itinerary, managing your own dashboard, monitoring your own listings: that is normal use. Operating someone else's account, or reaching past your authorization, is outside the scope of any legitimate use of this tool.
Third, no fraud, no ticket scalping, no bulk account registration. These uses violate terms of service in every case, and in many jurisdictions they are outright illegal.
Fourth, anti-detection is not a blanket exemption. This article has described the README's engineering honestly: an engine-level fingerprint, one consistent identity per seed, input events the page receives as trusted. Those are neutral technical facts. But "the page cannot detect it" does not mean "you may use it however you like"; the compliance of a tool is decided by the use, not by the implementation. If anyone promises you that a tool will never get blocked, treat it as marketing.
Pitfalls
Five reminders, all common sense, all regularly ignored.
One, the first run is slow. uvx pulls the repository and installs dependencies on first launch, and later runs are much faster. Give it time before you decide it is stuck.
Two, three rules for the key: never in git, never in screenshots, never in shared documents. If you suspect a leak, revoke the key on OpenRouter and create a new one. That costs a minute; a drained account costs considerably more.
Three, do not paste the placeholder. sk-or-... must be replaced with your full key. Run it verbatim and the failure arrives at model-call time.
Four, keep the routes separate. PowerShell commands pasted into a Linux shell, or bash commands pasted into PowerShell, fail on line one. On Windows, use PowerShell, not the legacy cmd.
Five, do not be stingy with task prompts. The sample task's four elements, URL, filters, range, and no-guessing, are a template, not decoration. Ten extra seconds writing the prompt saves the entire run.
FAQ
Q1: Do I need to install Python or set up a virtual environment first?
No. The README's route handles everything with uv: the install script sets up uv, then uvx pulls dots and its dependencies into an isolated environment on first launch. You need network access to astral.sh, GitHub and OpenRouter, plus an OpenRouter key.
Q2: Can I use a model outside OpenRouter?
Per the README, the model is any model on OpenRouter, startup requires --openrouter-key, and --model switches between models. The README documents no other model backend, and this article will not speculate about one.
Q3: How do I keep my logins between runs?
Pin the browser profile to a directory with --profile-dir. Logins and cookies persist across runs, so after the first sign-in, later tasks execute with the session already in place instead of walking the login flow again.
Q4: Can it serve as the browser for my Claude Code or Codex setup?
Yes. The same organization's invisible_playwright_mcp repository exposes this browser as an MCP server to Claude Code, Codex, Gemini CLI and other MCP clients; dots is its interface. Use dots --help for the full option list.
Q5: Is this project connected to OpenAI?
No. The README states it in its own words: Not affiliated with OpenAI. MIT licensed. The hosted Dots is OpenAI's product; the open-source dots is an independent community implementation, and the similar names do not make them related.
Once the three commands have run, what is the first task you will hand it?