Three steps: start a coordinator, enroll a machine, lease a browser.
-
-
-
Copy
-
# on a host with a reachable address
+
+
+
+
+ Quickstart
+ Up in three steps
+ Run a coordinator, enroll a machine, lease a browser. Everything below uses the binary installed by the one-liner above.
+
+
+
+
+
1 · Start the coordinator
+
+
+
+ sh
+ Copy
+
+
# on any host the workers can reach
devdooth coordinator --addr :8080 --store devdooth.db
-# prints a generated admin token once
-
+
# a generated admin token is printed once — save it
+
+
- 2 Join a machine
-
-
Copy
-
# mint a single-use enrollment token on the coordinator host
+
+
+
2 · Enroll and join a machine
+
+
+
+ sh
+ Copy
+
+
# on the coordinator host: mint a single-use enrollment token
devdooth enroll-token --url http://localhost:8080 --token <admin-token> --label macbook
# on the Mac / Pi / Linux box
@@ -497,54 +682,96 @@ 2 Join a machine
-
-
- The device identity is stored on the worker, so restarts need no token. The coordinator keeps
- only a hash. Inspect or revoke devices with devdooth devices.
-
-
-
3 Lease and drive
-
-
Copy
-
devdooth lease --url http://192.168.1.10:8080 --token <admin-token> \
+
+
+ The device identity is stored on the worker, so restarts need no token; the
+ coordinator stores only a hash. Inspect or revoke devices with
+ devdooth devices.
+
+
+
+
+
+
3 · Lease a browser
+
+
+
+ sh
+ Copy
+
+
devdooth lease --url http://192.168.1.10:8080 --token <admin-token> \
--profile shopping --headful --ttl 1800
-# → ws://192.168.1.10:8080/v1/lease/lse_x/cdp?token=...
-
-
-
-
# Python
+# → prints JSON, including "endpoint": "ws://192.168.1.10:8080/v1/lease/lse_x/cdp?token=…"
+
+
+
+
+
+
Drive it with Playwright
+
+
+
+
+ python
+ Copy
+
+
from playwright.async_api import async_playwright
+
async with async_playwright() as p:
browser = await p.chromium.connect_over_cdp(session.endpoint)
page = browser.contexts[0 ].pages[0 ]
await page.goto("https://example.com" )
-
-
+
+
+ node
+ Copy
+
+
import { chromium } from 'playwright-core' ;
+
const browser = await chromium.connectOverCDP(session.endpoint);
const page = browser.contexts()[0 ].pages()[0 ];
await page.goto('https://example.com' );
+
+
+
+ session.endpoint stands for the endpoint printed by
+ devdooth lease — there is no SDK. Copy the value into
+ your own variable and hand it to any CDP client.
+
-
- session.endpoint stands for the endpoint printed by
- devdooth lease. There is no SDK yet: copy that value (or set it in
- your own variable) and pass it to your CDP client.
-
-
- Use it from an AI agent
-
- Devdooth ships no browser tools of its own. devdooth mcp leases a
- browser, starts upstream Playwright MCP against it, and releases the lease when the MCP
- server exits. The default launcher runs npx -y @playwright/mcp,
- so Node.js (with npx) must be available where the MCP client runs.
-
-
-
Copy
-
// MCP client config (Claude Code, Cursor, VS Code, …)
-// run `command -v devdooth` to get the binary path
-{
+
+
+
+
+ Agents
+ Drive it from an AI agent
+
+ Devdooth ships no browsing tools of its own. devdooth mcp
+ leases a browser, starts the upstream Playwright MCP against it, and releases the
+ lease when the server exits.
+
+
+
+
+
+
+ json — MCP client config
+ Copy
+
+
{
"mcpServers" : {
"devdooth" : {
"command" : "/absolute/path/to/devdooth" ,
@@ -553,39 +780,63 @@ Use it from an AI agent
}
}
}
-
-
- Then ask the agent to browse. The browser it drives is running on your Mac, using that
- profile's logins and that machine's network.
-
-
-
# or point any CDP-aware library straight at a lease
-from browser_use import Browser
+
+
+ Works with Claude Code, Cursor, VS Code, and other MCP clients — run
+ command -v devdooth to fill in the binary path. The default
+ launcher runs npx -y @playwright/mcp, so Node.js (with
+ npx) must be available where the MCP client runs. Then just
+ ask the agent to browse: it drives a browser on your machine, with that profile's
+ logins and that machine's network.
+
+
+
+
+
+ python — or any CDP-aware library
+ Copy
+
+
from browser_use import Browser
+
browser = Browser(cdp_url=session.endpoint)
-
-
-
- Using a coding agent to set things up? Point it at
- AGENTS.md —
- it has a copy-paste task that builds, starts, leases, and verifies.
-
+
+
+
+
+ Using a coding agent for the setup itself? Point it at
+ AGENTS.md —
+ it contains a copy-paste task that builds, starts, leases, and verifies the whole system.
+
+
-
- Persistent profiles
-
-
-
- Profiles live at ~/.devdooth/profiles/<name> on the worker (--data-dir overrides the location).
- One profile is opened by at most one browser at a time; a second request fails explicitly.
- Profile data stays on its worker and is never copied between machines. A profile-only request matches any online worker advertising that name; pass --node with --profile to pin a specific machine.
- Complete a login or MFA once in headful mode, and later leases reuse it.
+
+
+
+
+ Profiles
+ Log in once, keep the profile
+
+ Profiles are named, worker-local Chrome directories. Declare them when the worker
+ starts, then request them by name at lease time.
+
+
+
+
+ Profiles live at ~/.devdooth/profiles/<name> on the worker; --data-dir relocates them.
+ One browser at a time opens a profile — a concurrent request fails explicitly instead of corrupting it.
+ Profile data never leaves or moves between machines. A profile-only request matches any online worker advertising that name; add --node to pin a specific machine.
+ Complete a login or MFA once in headful mode, and every later lease on that profile reuses it.
-
-
-
-
devdooth worker --coordinator ... --profiles shopping,work
+
+
+ sh
+ Copy
+
+
devdooth worker --coordinator ... --profiles shopping,work
devdooth lease --url http://192.168.1.10:8080 --token <admin-token> \
--node macbook --profile shopping --headful
@@ -595,108 +846,134 @@ Persistent profiles
-
- Security model
-
- A leased browser may be logged into your email, bank, or internal services. Devdooth treats
- raw browser access as the powerful capability it is.
-
-
-
-
What it does
-
- Outbound-only workers; the browser's debug port binds to loopback.
- Separate admin and worker credentials. Equal-length admin/static-worker tokens are compared with a full scan; enrolled device tokens are authenticated by hash lookup.
- Single-use, expiring enrollment tokens; only hashes stored.
- Per-lease credentials bounded by the lease TTL.
- Worker enforces the TTL even if the coordinator is gone.
- Bounded buffers; stalled peers are disconnected.
- The relay never logs CDP payloads. Normal lifecycle logs omit credentials; development startup and credential-recovery paths deliberately print a token so it is not lost.
-
-
-
-
What it will not do
-
- Protect you from a compromised coordinator.
- Make an authorized CDP client safe: it can read local files and reach local-network services.
- Treat CDP filtering as a security boundary.
- Share one coordinator safely across untrusted owners (single-owner V1).
-
+
+
+
+
+ Security
+ An honest security model
+
+ A leased browser may be logged into your email, bank, or internal services.
+ Devdooth treats raw browser access as the powerful — and dangerous — capability it is.
+
+
+
+
+
What it does
+
+ Workers are outbound-only; each browser's debug port binds to loopback.
+ Separate admin and worker credentials. Equal-length tokens are compared with a full scan; enrolled device tokens are authenticated by hash lookup.
+ Enrollment tokens are single-use and expiring, and only their hashes are stored.
+ Per-lease credentials are bounded by the lease TTL, which the worker enforces even if the coordinator is gone.
+ Buffers are bounded and stalled peers are disconnected; the relay never logs CDP payloads.
+
+
+
+
What it does not do
+
+ Protect you from a compromised coordinator.
+ Make an authorized CDP client safe — it can read local files and reach local-network services.
+ Treat CDP filtering as a security boundary.
+ Share one coordinator across untrusted owners — Devdooth is single-owner.
+
+
+
+ For real isolation, run workers under a separate OS account, container, or VM, and
+ advertise only the profiles you are willing to lend. Terminate TLS at a reverse proxy
+ for non-local deployments and set --public-url https://….
+ The full threat model is
+ in the repo .
+
-
- For real isolation, run workers under a separate OS account, container, or VM, and only
- expose the profiles you are willing to lend. Terminate TLS at a reverse proxy for
- non-local deployments and set --public-url https://….
-
-
- Reference
-
- CLI
-
+
+
+
+
+
+
+
+
CLI
+
+
+
+
+ Command Purpose
+
+
+ devdooth coordinatorRun the control plane and relay.
+ devdooth enroll-tokenMint a single-use enrollment token.
+ devdooth joinEnroll this machine and store its device identity.
+ devdooth workerConnect outbound and lend this machine's browsers.
+ devdooth nodesList connected workers and capabilities.
+ devdooth devicesList or revoke enrolled devices.
+ devdooth lease / releaseAcquire or release a browser.
+ devdooth pause / resumeHand a headful session to a human, then back.
+ devdooth mcpLease a browser and supervise Playwright MCP.
+
+
+
+
-
HTTP API
-
+
+
+
HTTP API
+
+
+
+
+ Method Path Purpose
+
+
+ GET /healthzLiveness.
+ GET /v1/nodesConnected workers and capabilities.
+ POST /v1/leasesAcquire a browser.
+ GET /v1/leases/{id}Lease state.
+ DELETE /v1/leases/{id}Release, waiting for teardown.
+ POST /v1/leases/{id}/pauseDetach the controller for human take-over.
+ POST /v1/leases/{id}/resumeAllow a controller to attach again.
+ GET /v1/lease/{id}/cdpCDP WebSocket endpoint.
+ POST /v1/enroll-tokensMint an enrollment token.
+ POST /v1/enrollRedeem a token for a device identity.
+ GET /v1/devicesList enrolled devices.
+ DELETE /v1/devices/{id}Revoke a device and drop its connection.
+
+
+
+
-
Limitations
-
- Chromium-family browsers only (CDP).
- One browser process per lease; no multi-context packing.
- Single owner; no ACLs.
- Workers use installed browsers; Devdooth downloads none.
- Human take-over is cooperative (pause/resume), not enforced ownership.
- Remote uploads and downloads vary by browser; verify them per use case.
-
+
+
+
Limitations
+
+
+ Chromium-family browsers only (CDP).
+ One browser process per lease; no multi-context packing.
+ Single owner; no ACLs.
+ Workers use installed browsers — Devdooth downloads none.
+ Human take-over is cooperative (pause/resume), not enforced ownership.
+ Remote uploads and downloads vary by browser; verify them per use case.
+
+
+
+
-