How CrawlGenie works — a walkthrough for the owner
Modes, a session end to end, routines, profiles, secrets, how to test.
You run one server (Docker image). It owns a pool of Chromium browsers. Two kinds of users talk to it:
- Humans use the dashboard (
https://your-domain/) to watch, approve, replay, teach routines and manage keys/secrets. - Agents / code use the API (
/v1/...), MCP (/mcp) or the function-calling tools (/v1/tools). They never see the dashboard; they get compact snapshots and act byref.
Who can use it: two modes#
| Self-hosted (default) | Cloud mode (VESSEL_SIGNUP=true) | |
|---|---|---|
| Who gets in | Keys you put in VESSEL_API_KEYS | Anyone who signs up in the dashboard (email + password) |
| Where accounts live | nowhere — keys are config | users, orgs, api_keys tables in /data/vessel.db |
| Isolation | all keys see everything (optionally quotas per key) | each organisation sees only its own sessions, profiles, routines, secrets, keys; env keys are admins |
| Keys | fixed | minted/revoked in Setup → API keys, shown once, stored hashed |
So: yes, the database is in-house. Sign-ups are rows in SQLite on your VM; nothing goes to a third party.
A session, end to end#
- Create —
POST /v1/sessions(or the New session button). Everything is optional:url– where to start.tier–shared(pooled Chromium, fastest, default) orisolated(own Chromium + raw CDP).profile– resume a saved login;profileLockexclusive by default.proxy–{ pool, country, sticky }or your own{ server, username, password }.policy– the guardrails:allowDomains,denyDomains,blockResources(images/media/fonts),maxActions,maxDurationSec,idleTimeoutSec,maxBytes,approvals: { mode: off|destructive|all, patterns },record.snapshotDefaults–{ mode: interactive|full, maxChars, delta }.viewport,userAgent,locale,timezoneId,extensions(isolated),metadata(labels for the dashboard). You get backSessionInfo:id,liveUrl(dashboard page),cdpUrl(isolated),expiresAt,usage.
- Observe —
GET /v1/sessions/:id/snapshotreturns the page as a short accessibility tree with[ref=eN]markers (2–8k chars instead of 50–500k of HTML).delta=truereturns only what changed. - Act —
POST /v1/sessions/:id/act { actions: [...] }withclick/fill/type/press/select/check/scroll/ upload/goto/wait/assert/evaluate/tab/dialogbyref. The response includes the next snapshot, so one round trip per step. Credentials go in as{{secret:name}}. - Guardrails run server-side — a navigation outside
allowDomainsis blocked; a click that looks destructive (pay/send/delete, or yourpatterns) is held until a human approves it in the dashboard (or viaPOST .../approvals/:aid, or your webhook). - Everything is recorded — each step with a screenshot and storage state. The dashboard shows the live screen (WebSocket screencast), the timeline, the exact snapshot the model saw, usage and the network log. You can take over with mouse/keyboard, fork a new session from any step, or save as routine.
- Close —
DELETE /v1/sessions/:id, or idle/max timers do it.usagereports actions, bandwidth, observation tokens read vs raw, and the estimated dollars saved.
Routines, profiles, secrets, one-shots#
- Routine = a recorded flow with
{{params}}; replays re-find elements by role + name (self-healing), cost zero model tokens, and can carryassertsteps so they double as monitors. Run from the dashboard,POST /v1/routines/:name/run, or thebrowser_run_routinetool. - Profile = cookies + localStorage saved from a session; pass
profileon create to resume logged in. - Secret = a credential stored server-side; the model types the placeholder, the server types the value.
- One-shot =
POST /v1/scrape,GET /v1/screenshot,GET /v1/pdfwhen you only want the page.
How to test it end to end right now#
On your own machine (already running at http://localhost:7070 during development) or on the VM after deploy/install.sh:
- Open the dashboard → Get started. Click New session, URL
https://news.ycombinator.com, approvalsdestructive. Watch the live view; open Snapshot and note the token count vs raw. - Click Take over, log in to any site you own, then Save profile. Create another session with that profile: it starts logged in.
- Scrub the Timeline, click Save as routine, then run it from Routines — zero model tokens.
- Secrets → add
demo_pw; in a session,filla password field with{{secret:demo_pw}}(via API or MCP) and confirm the recording shows only the placeholder. - Hand it to a model:
claude mcp add --transport http vessel http://localhost:7070/mcp --header "Authorization: Bearer <key>"and ask Claude to "open example.com and tell me the heading". Orpip install vessel-browserand runpackages/python/tests/smoke.py. - Cloud mode: start with
VESSEL_SIGNUP=true, open the dashboard, sign up, mint a key under API keys, and repeat step 5 with that key. Sign up a second account and confirm it cannot see the first one's sessions.
Section: Build · This page as markdown · Questions? hello@crawlgenie.com